【问题标题】:Java volatile variables affecting memory consistency of other non-volatile variablesJava volatile 变量影响其他非易失性变量的内存一致性
【发布时间】:2017-05-01 00:10:46
【问题描述】:
场景 A
A1。写入 volatile 变量
A2。将所有本地非易失性变量写入主存
场景 B
B1。从 volatile 变量中读取
B2。将所有非易失性变量从主内存重新加载到本地内存
- 场景 A 和 B 是否是 volatile 所涉及的正确行为
变量?或者场景A也包括B2,或者场景B
还包括A2?
- 这些场景是原子的吗?还能发生什么
在A1和A2之间?还是 B1 和 B2?
(使用 Java 1.8 / 1.5+)
【问题讨论】:
标签:
java
synchronization
shared-memory
volatile
memory-corruption
【解决方案1】:
写入易失性变量不保证刷新非易失性变量1。但是,它将在对 volatile 的写入与对 volatile 的任何后续读取之间引入“发生在之前”的关系(假设没有干预写入)。您可以按如下方式利用它:
- 线程 A:写入 NV
- 线程 A:写入 V
- 线程 B:读取 V
- 线程 B:读取 NV
如果操作按此顺序发生,那么线程 B 将在第 4 步中看到 NV 的更新值。但是,如果在第 2 步之后有东西(包括 A)写入 NV,则未指定线程 B 在第 4 步将看到什么.
一般来说,以这种方式使用 volatile 需要深入而仔细的推理。使用synchronized 更简单、更健壮。
你的例子不清楚:
如果它旨在描述 Java 程序员必须做什么,那是错误的/荒谬的。 Java 代码不能刷新变量。
如果它打算成为在实现级别(例如在 JIT 编译代码中)发生的必须的规范,那也是错误的。
如果它旨在描述在实现级别(例如在 JIT 编译的代码中)可能发生的事情,那么它是正确的。
我不只是在这里学究气。编译器可能决定它不需要在线程 A 中刷新 all 本地非易失性数据,并且很可能只在线程 B 中重新加载它需要的非易失性数据。它是如何决定的?那是编译器作者的事!
1 - JLS 不需要硬件特定的操作,例如刷新。相反,它要求编译后的代码满足一些特定的内存可见性保证,并将实现留给编译器编写者。