发生在 volatile 之前和重新排序

Mar*_*234 6 java multithreading java-memory-model jls

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

int value;
volatile boolean ready;

// ...

value = 1;     // (1)
ready = true;  // (2)
Run Code Online (Sandbox Code Playgroud)

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

如果 x 和 y 是同一线程的动作,并且 x 在程序顺序中排在 y 之前,则 hb(x, y)。

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

[...] 允许编译器重新排序任一线程中的指令,前提是这不会影响该线程的单独执行。

这显然是这里的情况。

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

对 volatile 字段(第 8.3.1.4 节)的写入发生在该字段的每次后续读取之前。


在人们看到 volatile (读或写)的使用可能不会重新排序的保证时,我感到困惑。

您能否将您的解释基于 JLS 或基于 JLS 的其他来源。

Mal*_*alt 1

孤立地看,您的代码不能保证任何事情。这里涉及第二个线程,我们也需要它的代码!您链接的教程显示这两个线程是有原因的。

\n\n

如果两个线程的代码是这样的:

\n\n
int value;\nvolatile boolean ready;\n\n// Thread - 1\nvalue = 1;     // (1)\nready = true;  // (2)\n\n// Thread - 2\nif (ready) {  // (3)\n    x = value // (4)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,由于程序顺序,我们在 (1) 和 (2) 之间存在先发生关系:

\n\n
\n

如果 x 和 y 是同一线程的操作,并且 x 按程序顺序出现在 y 之前,则 hb(x, y)。

\n
\n\n

ready由于不稳定,我们在 (2) 和 (3) 之间存在先发生关系:

\n\n
\n

对易失性字段 (\xc2\xa78.3.1.4) 的写入发生在该字段的每次后续读取之前。

\n
\n\n

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

\n\n
\n

如果 x 和 y 是同一线程的操作,并且 x 按程序顺序出现在 y 之前,则 hb(x, y)。

\n
\n\n

所以有一个发生之前链 (1) \xe2\x86\x92 (2), (2) \xe2\x86\x92 (3), (3) \xe2\x86\x92 (4)

\n\n

由于“发生之前”是传递关系(如果 A 发生在 B 之前,B 发生在 C 之前,则 A 发生在 C 之前),这意味着 (1) 发生在 (4) 之前。

\n\n

如果我们翻转 (3) 和 (4) 以便第二个线程value在读取 之前读取ready,则发生之前链将中断,并且我们不再获得有关从 读取的任何保证value

\n\n

这是一篇很好的教程,其中包含更多 JMM 陷阱,包括这个。

\n\n

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

\n