java – 发生之前和volatile的重新排序

有多个代码示例,假设以下指令(1)和(2)不能重新排序:

int value;
volatile boolean ready;

// ...

value = 1;     // (1)
ready = true;  // (2)

> “What Volatile Means In Java”
> “Details zu volatile-Variablen”(德语)
> Stack Overflow answer

后一个Stack Overflow应答是指JLS §17.4.5

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).

但是我不明白为什么这应该适用于此,因为JLS Example 17.4-1还指出:

[…] compilers are allowed to reorder the instructions in either thread, when this does not affect the execution of that thread in isolation.

这显然就是这种情况.

特定于volatile的JLS中的所有其他定义仅针对相同的volatile变量,而不针对其他操作:

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

令我困惑的是人们看到保证不能重新排序易失性(读或写)的使用.

您能否根据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)之间有一个先发生的关系:

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).

由于存在易变性,我们在(2)和(3)之间存在一个先发生的关系:

A write to a volatile field (§8.3.1.4) happens-before every subsequent
read of that field.

由于程序顺序的原因,我们在(3)和(4)之间有一个先发生过的关系:

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).

所以有一个发生前链(1)→(2),(2)→(3),(3)→(4)

并且因为发生之前是传递关系(如果A发生在B和B发生在C之前,那么A发生在C之前)这意味着(1)发生在(4)之前.

如果我们翻转(3)和(4)以便第二个线程在读取就绪之前读取值,那么发生之前的链断裂,我们不再对从值的读取得到任何保证.

这是一个不错的tutorial,还有一些JMM陷阱,包括这个陷阱.

Java内存模型不是很有趣吗?

点赞