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 的其他来源。
孤立地看,您的代码不能保证任何事情。这里涉及第二个线程,我们也需要它的代码!您链接的教程显示这两个线程是有原因的。
\n\n如果两个线程的代码是这样的:
\n\nint 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\n\n如果 x 和 y 是同一线程的操作,并且 x 按程序顺序出现在 y 之前,则 hb(x, y)。
\n
ready
由于不稳定,我们在 (2) 和 (3) 之间存在先发生关系:
\n\n\n对易失性字段 (\xc2\xa78.3.1.4) 的写入发生在该字段的每次后续读取之前。
\n
由于程序顺序,我们在 (3) 和 (4) 之间有一个发生在之前的关系:
\n\n\n\n\n如果 x 和 y 是同一线程的操作,并且 x 按程序顺序出现在 y 之前,则 hb(x, y)。
\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
。
这是一篇很好的教程,其中包含更多 JMM 陷阱,包括这个。
\n\nJava 内存模型是不是很有趣?
\n