Java内存模型:挥发物和读/写重新排序
我在理解JMM如何与可能的指令重新排序相关时遇到问题。例如,让我们考虑下面的代码片段:Java内存模型:挥发物和读/写重新排序,java,concurrency,java-memory-model,Java,Concurrency,Java Memory Model,我在理解JMM如何与可能的指令重新排序相关时遇到问题。例如,让我们考虑下面的代码片段: volatile boolean t1HasArrived = false, t2HasArrived = false; // In thread 1 t1HasArrived = true; while (!t2HasArrived) ; // In thread 2 t2HasArrived = true; while (!t1HasArrived) ; 现在,我想相信这段代码实现了
volatile boolean t1HasArrived = false, t2HasArrived = false;
// In thread 1
t1HasArrived = true;
while (!t2HasArrived)
;
// In thread 2
t2HasArrived = true;
while (!t1HasArrived)
;
现在,我想相信这段代码实现了两个线程的屏障同步。但我不确定。让我怀疑的是读/写的重新排序:JMM中是否有任何东西会阻止编译器或CPU像下面的代码片段那样重新排列代码的执行路径?如果没有,你如何证明这种重新排序是允许的
// In thread 1
while (!t2HasArrived)
;
t1HasArrived = true;
// In thread 2
while (!t1HasArrived)
;
t2HasArrived = true;
注意,我并没有试图实现无锁屏障。这只是我开始考虑指令重新排序后想到的一个例子。我只是想了解如何将JMM规则应用于它。当只涉及一个易失性变量/锁时,对代码进行推理相对容易,但当涉及多个易失性变量/锁时,事情就变得复杂了。易失性变量不能通过JMM相互重新排序 在您的例子中,您有一个volatile存储,后面跟一个volatile加载,并且这些存储不能被重新排序到后面跟一个存储的加载中。这适用于所有版本的Java 有关详细信息,请参阅
奥尼·斯佩兰扎小姐,您好 发件人: §5定义 线程间操作线程间操作是由一个线程执行的操作,可以被另一个线程检测到或直接影响。线程间操作包括读取和写入共享变量以及同步操作,例如锁定或解锁监视器、读取或写入易失性变量或启动线程 同步操作同步操作包括锁定、解锁、读取和写入易失性变量、启动线程的操作以及检测线程已完成的操作 §7.3格式良好的处决 我们只考虑良好的处决。一次执行E=〈通讯社 po→, 所以→, W、 V,sw→, 血红蛋白→〉 如果以下条件为真,则为格式良好:
volatile
字段暗示了观察volatile write值的任何volatile read的同步顺序。同步顺序要求线程按照程序顺序观察在易失性读取之后在易失性写入之前提交的所有写入字段
这意味着,每当读取volatile字段时,应用程序的程序顺序要求根据程序顺序提交volatile写操作,这会导致写操作与读操作之间的关系发生变化。因此,您建议作为优化的重新排序是无效的,JMM保证了原始源代码所暗示的可见性
如果你想更详细地理解这一点,我最近给出了一个答案(时间-7小时27分钟)。我对你的问题不是很确定。但是从java并发性书籍中,我相信有人提到读取volatile变量就像进入同步块一样,这意味着从另一个线程修改的volatile变量的值将对您可见,另一个线程的代码执行顺序也会变得像是按顺序执行的一样。Leonid我同意CChi的观点,很难说你想要回答的是什么问题尽管你的断言是正确的,请注意,您的答案是基于实现Java内存模型的建议,该模型比JMM允许的要严格得多。因此,引用这本食谱并不能真正证明任何事情。@Rafael请编辑答案,如果你有更好的来源,请添加引用!好吧,我想这从实用的角度回答了我的问题,但我仍然很好奇JMM中的哪些词禁止这种重新排序。@Leonid我刚刚添加了JSR-133的几个摘录。它们有点技术性,我希望您能阅读它们。@Tomas这需要替换整个答案。正如我所说的,食谱是对实现者的一个指导,它是一个明智的实现,它大大超过了JMM。JMM以排序的形式而不是内存围栏的形式进行争论。您提供的链接将导致8小时的JavaOne直播视频。在7点27分,你可以看到斯图尔特·马克斯正在为他的“20年API”演讲做准备(刚刚看完,很好的历史演讲),所以,我想,你演讲的时间已经改变了。7小时27分钟。不幸的是,JavaOne只上传了未剪辑的视频。