有多个代码示例,假设以下指令(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内存模型不是很有趣吗?