我在理解JMM如何与可能的指令重新排序有关时遇到了问题。例如,让我们考虑以下代码片段:
volatile boolean t1HasArrived = false, t2HasArrived = false;
// In thread 1
t1HasArrived = true;
while (!t2HasArrived)
;
// In thread 2
t2HasArrived = true;
while (!t1HasArrived)
;
现在,我想相信这段代码为两个线程实现了Barrier同步。但我不确定。让我怀疑的是读/写重新排序:JMM中有什么东西会阻止编译器或CPU重新排列代码的执行路径,如下面的代码片段?如果没有,你如何证实这种重新排序是允许的呢?
// In thread 1
while (!t2HasArrived)
;
t1HasArrived = true;
// In thread 2
while (!t1HasArrived)
;
t2HasArrived = true;
注意,我并没有尝试实现无锁障碍。在我开始考虑重新排序指令之后,这只是我想到的一个例子。我只是想了解如何将JMM规则应用于它。当只涉及一个易失性变量/锁时,相对容易推理一段代码,但是当有几个时,事情变得复杂。
答案 0 :(得分:4)
JMM不能将易失性变量重新排序。
在您的情况下,您有一个易失性存储,后跟易失性负载,并且这些存储无法重新排序到负载中。对于所有版本的Java都是如此。
有关详细信息,请参阅The JSR-133 Cookbook for Compiler Writers。
Lasciate ogni speranza,voi ch'entrate。
线程间操作线程间操作是由一个线程执行的操作,可以被另一个线程检测到或直接受其影响。线程间操作包括读取和写入共享变量和同步操作,例如锁定或解锁监视器,读取或写入volatile变量或启动线程。
同步操作同步操作包括锁定,解锁,读取和写入易失性变量,启动线程的操作以及检测线程已完成的操作。
我们只考虑结构良好的执行。执行E =
形成良好:
- 同步顺序与程序顺序和互斥一致。同步顺序与程序顺序一致意味着顺序发生的顺序,由与边缘和程序顺序同步的传递闭包给出,有效的偏序:反身,传递和反对称。使同步顺序与互斥一致意味着在每个监视器上,锁定和解锁操作都是正确嵌套的。
醇>
答案 1 :(得分:3)
JMM将重新排序限制定义为某些程序指令顺序的传递闭包。如果在write和read之间存在排序,则JVM需要根据此顺序返回值。
在您的情况下,字段为{{隐含易失性读取的同步顺序,以观察易失性写入的值1}}。同步顺序,需要一个线程来观察在按程序顺序进行易失性读取之后在易失性写入之前编译的所有写入字段。
这意味着每当读取一个易失性字段时,应用程序的程序顺序就要求根据程序顺序对易失性写入进行评估,这会导致写入与读取的发生 - 之前关系。因此,您建议作为优化的重新排序无效,JMM保证原始源代码所暗示的可见性。
如果您想更详细地了解这一点(时间 - 7小时27分钟),我最近给了presentation on the JMM。