您正在使用 System.out.println 在内部有一个 synchronized(this) {...} 那会让事情变得更糟。但即使这样,你的读者线程仍然可以观察到 1, 0 ,即:一个活泼的读物。 到目前为止,我还不是这方面的Maven,但在浏览了阿列克谢·希皮列夫(alexey shipilev)的大量视频/例子/博客之后,我想我至少了解了一些东西。 jls声明: 如果x和y是同一线程的动作,并且x按程序顺序排在y之前,那么hb(x,y)。 因为两人都读到了 var 在 program order ,我们可以得出:
(po)
firstRead(var) ------> secondRead(var)
// po == program order
这句话还说,这建立了一个 happens-before 顺序,所以:
(hb)
firstRead(var) ------> secondRead(var)
// hb == happens before
但这是“同一条线”。如果我们想对多个线程进行推理,我们需要研究同步顺序。我们需要这个,因为同一段关于 happens-before order 说: 如果一个动作x与后面的动作y同步,那么我们还有hb(x,y)。 所以如果我们在 program order 以及 synchronizes-with order ,我们可以对结果进行推理。我们将其应用于代码:
(NO SW) (hb)
write(var) ---------> firstRead(var) -------> secondRead(var)
// NO SW == there is "no synchronizes-with order" here
// hb == happens-before
这就是 happens-before consistency 在同一章中起作用: 如果对于a中的所有读取r,其中w(r)是r看到的写操作,则a中的一组操作a在一致之前发生,而不是hb(r,w(r))或存在w.v=r.v且hb(w(r),w)和hb(w,r)。 在一组“在发生之前”一致的操作中,每次读取都会看到一个“在排序之前发生”允许它看到的写入 我承认我很模糊地理解了第一句话,这就是亚历克赛帮助我最多的地方,正如他所说: 读取或看到发生在 happens-before 或任何其他文字。 因为没有 synchronizes-with order 在那里,隐含着没有 happens-before order ,允许读取线程通过race读取。从而得到 1 ,比 0 . 一旦你介绍了一个正确的 synchronizes-with order ,例如这里的一个 监视器m上的解锁操作与所有后续锁定操作同步。。。 对易失性变量v的写入与任何线程对v的所有后续读取同步。。。 图形会发生变化(假设您选择 var volatile ):
SW PO
write(var) ---------> firstRead(var) -------> secondRead(var)
// SW == there IS "synchronizes-with order" here
// PO == happens-before
``` `PO` (程序顺序)给出 `HB` (发生在之前)通过我在回答中引用的第一句话。以及 `SW` 给予 `HB` 因为:
如果一个动作x与后面的动作y同步,那么我们还有hb(x,y)。
像这样的:
4条答案
按热度按时间mm9b1k5b1#
加上其他答案:
与
long
以及double
,写入可能不是原子的,因此前32位可能在最后32位之前可见,反之亦然。因此可以输出完全不同的值。w8biq8rn2#
我认为这里缺少的是这样一个事实:这些线程运行在实际的物理内核上,我们这里几乎没有可能的变体:
所有线程都在同一个内核上运行,那么问题就归结为这3条指令的执行顺序,在这种情况下1,0是不可能的,我认为println的执行顺序是由于同步产生的内存障碍,所以排除了1,0
a和b在两个不同的核上运行,那么1,0看起来也不可能,只要运行线程a的核读取1,就不可能在之后读取0,就像上面订购的printlns一样。
线程a在这两个println之间被重新调度,因此第二个println在不同的内核上执行,或者与b曾经/将要执行的相同,或者在不同的第三个内核上执行。因此,当两个println在不同的核上执行时,这取决于两个核看到的值,如果var不同步(不清楚var是其中的一个成员),那么这两个核可以看到不同的var值,因此有可能为1,0。
所以这是一个缓存一致性问题。
p、 我不是一个jvmMaven,所以这里可能还有其他事情要做。
yrefmtwq3#
当
var
是读出来的1
不会变回来的。无论是由于可见性还是重新排序,此输出都不会发生。可能发生的是0 0
,0 1
以及1 1
.这里要理解的关键是
println
包括同步。看看里面的方法,你应该看到一个synchronized
在那里。这些块的效果是打印将按顺序进行。尽管写入操作随时都可能发生,但第一次打印不可能看到var
但是第二个印刷品看到了旧的价值。因此,写入只能发生在两个打印之前、打印之间或打印之后。除此之外,也不能保证写操作是可见的
var
未标记为volatile
写入也没有以任何方式同步。7rtdyuoh4#
您正在使用
System.out.println
在内部有一个synchronized(this) {...}
那会让事情变得更糟。但即使这样,你的读者线程仍然可以观察到1, 0
,即:一个活泼的读物。到目前为止,我还不是这方面的Maven,但在浏览了阿列克谢·希皮列夫(alexey shipilev)的大量视频/例子/博客之后,我想我至少了解了一些东西。
jls声明:
如果x和y是同一线程的动作,并且x按程序顺序排在y之前,那么hb(x,y)。
因为两人都读到了
var
在program order
,我们可以得出:这句话还说,这建立了一个
happens-before
顺序,所以:但这是“同一条线”。如果我们想对多个线程进行推理,我们需要研究同步顺序。我们需要这个,因为同一段关于
happens-before order
说:如果一个动作x与后面的动作y同步,那么我们还有hb(x,y)。
所以如果我们在
program order
以及synchronizes-with order
,我们可以对结果进行推理。我们将其应用于代码:这就是
happens-before consistency
在同一章中起作用:如果对于a中的所有读取r,其中w(r)是r看到的写操作,则a中的一组操作a在一致之前发生,而不是hb(r,w(r))或存在w.v=r.v且hb(w(r),w)和hb(w,r)。
在一组“在发生之前”一致的操作中,每次读取都会看到一个“在排序之前发生”允许它看到的写入
我承认我很模糊地理解了第一句话,这就是亚历克赛帮助我最多的地方,正如他所说:
读取或看到发生在
happens-before
或任何其他文字。因为没有
synchronizes-with order
在那里,隐含着没有happens-before order
,允许读取线程通过race读取。从而得到1
,比0
.一旦你介绍了一个正确的
synchronizes-with order
,例如这里的一个监视器m上的解锁操作与所有后续锁定操作同步。。。
对易失性变量v的写入与任何线程对v的所有后续读取同步。。。
图形会发生变化(假设您选择
var
volatile
):HB HB
write(var) ---------> firstRead(var) -------> secondRead(var)
@JCStressTest
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "Doing both reads early.")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "Doing both reads late.")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "Doing first read early, not surprising.")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "First read seen racy value early, and the second one did not.")
@State
public class SO64983578 {
}