繁体   English   中英

发生之前和重新排列挥发物

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM