Java volatile变量影响其他非volatile变量的内存一致性

标签 java synchronization shared-memory volatile memory-corruption

场景A

A1。写入 volatile 变量

A2。刷新所有本地非 volatile 变量写入到主内存

场景 B

B1。从 volatile 变量中读取

B2。将所有非 volatile 变量从主存重新加载到本地内存

  • 场景 A 和 B 是涉及 volatile 的正确行为吗 变量?或者场景A是否也包含B2,或者场景B是否也包含 还包括A2?
  • 这些场景是原子的吗?会不会有其他事情发生 在A1和A2之间?还是 B1 和 B2?

(使用 Java 1.8/1.5+)

最佳答案

写入 volatile 变量保证刷新非 volatile 变量1。但是,它会在对 volatile 的写入和对 volatile 的任何后续读取之间引入“发生在之前”的关系(假设没有对其进行干预写入)。您可以按如下方式利用它:

  1. 线程A:写NV
  2. 线程A:写入V
  3. 线程 B : 读取 V
  4. 线程 B : 读取 NV

如果操作按该顺序发生,则线程 B 将在步骤 4 中看到 NV 的更新值。但是,如果某些东西(包括 A)在步骤 2 之后写入 NV,则线程 B 将在步骤 4 中看到什么是不确定的.

一般来说,以这种方式使用 volatiles 需要深入而仔细的推理。使用 synchronized 更容易也更健壮。


你的例子不清楚:

  • 如果它旨在描述 Java 程序员必须做什么,那是错误的/荒谬的。 Java 代码不能刷新变量。

  • 如果它旨在成为在实现级别(例如在 JIT 编译代码中)必须发生的事情的规范,那也是错误的。

  • 如果它旨在描述在实现级别(例如在 JIT 编译代码中)可能发生的事情,那么它是正确的。

我不只是在这里学究气。编译器可能会决定它不需要刷新线程 A 中的所有 局部非 volatile 变量,并且它很可能只会重新加载它在线程 B 中需要的那些。它是如何决定的?那是编译器作者的事!


1 - JLS 不需要硬件特定的操作,例如刷新。相反,它要求编译后的代码满足一些特定的内存可见性保证,并将实现留给编译器编写者。

关于Java volatile变量影响其他非volatile变量的内存一致性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43712673/

相关文章:

java - 我是否正确阅读了这个顶级命令?

JAVA - 检查值是否不在其他索引二维数组中

sql-server-2008-r2 - 阻止 Sync Framework 同步删除

linux - 共享内存 : what's the difference between the key and the id?

synchronization - 使用 WinSCP 将今天的文件从远程目录同步到本地目录

java - Java 监视器有多重?

c - 使用映射共享内存获取 "out-of-bounds"和 "variable uninitialized"警告

java - Android HTTP 请求在模拟器中工作但不在磨损设备上

java - 来 self 的自定义 servlet 的 Kerberos 身份验证?

java - Camel 日志组件