[英]Happens-before and reordering of volatile
有多个代码示例假定以下说明(1)和(2)不能重新排序:
int value;
volatile boolean ready;
// ...
value = 1; // (1)
ready = true; // (2)
后面的堆栈溢出答案参考JLS§17.4.5 :
如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。
但是我不明白为什么这应该在这里适用,因为JLS 示例17.4-1还指出:
允许编译器在不影响单独执行该线程的情况下,对每个线程中的指令重新排序。
这显然是事实。
JLS中针对volatile
所有其他定义仅针对同一volatile变量,而不针对其他动作:
在随后每次对该字段进行读取之前,都会写入一个易失字段(第8.3.1.4节)。
这使我感到困惑,因为人们看到了对保证不重新排列易失性(读或写)的使用的保证。
您能否以JLS或其他基于JLS的资料为基础进行解释。
孤立地,您的代码不能保证任何事情。 这里涉及第二个线程, 我们也需要它的代码 ! 您链接的教程同时显示两个线程是有原因的。
如果两个线程的代码是这样的:
int value;
volatile boolean ready;
// Thread - 1
value = 1; // (1)
ready = true; // (2)
// Thread - 2
if (ready) { // (3)
x = value // (4)
}
然后,由于程序顺序,我们在(1)和(2)之间有一个事前发生的关系:
如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。
并且由于ready
易失,我们在(2)和(3)之间具有先发生后关系:
在随后每次对该字段进行读取之前,都会写入一个易失字段(第8.3.1.4节)。
再次由于程序顺序,我们在(3)和(4)之间具有事前发生的关系:
如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。
因此,在链(1)→(2),(2)→(3),(3)→(4)之前发生了
并且由于事前发生是传递关系(如果A发生在B之前,B发生在C之前,那么A发生在C之前),这意味着(1)发生在(4)之前。
如果翻转(3)和(4),以便第二个线程在读取ready
之前先读取value
,那么在链发生之前,before-before会中断,并且我们再也无法获得对value
的读取的保证。
这是一个不错的教程 ,其中包含更多JMM陷阱。
Java内存模型不是很有趣吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.