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

时间:2017-05-01 00:10:47

标签: java synchronization shared-memory volatile memory-corruption

情景A

A1。写入易失性变量

A2。刷新所有本地非易失性变量写入主存储器

情景B

B1。从易变变量中读取

B2。将所有非易失性变量从主存储器重新加载到本地存储器

  • 情景A和B是否与volatile有关的正确行为 变量?或者场景A也包括B2,或场景B 还包括A2?
  • 这些场景是原子的吗?可以发生任何事情 在A1和A2之间?还是B1和B2?

(使用Java 1.8 / 1.5 +)

2 个答案:

答案 0 :(得分:2)

实际规则是“写入易失性变量 v (§8.3.1.4) v 的所有后续读取同步任何线程(根据同步顺序定义“后续”)。“ http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4

换句话说,从一个线程写入到v的写入一旦在该写入之后读取v,就会对另一个线程的读取都可见。

我不确定“冲到主要”是理解这一点的必要方式。 Java内存模型以发生在之前和同步的方式记录。我建议用这些术语来考虑它。从概念上讲,如果JVM不是必需的,那么它可以省略某些“刷新”。

答案 1 :(得分:2)

写入易失性变量保证刷新非易失性变量 1 。但是,它会在写入易失性和随后的易失性读取之间引入“先发生”关系(假设没有中间写入)。您可以按如下方式利用它:

  1. 主题A:写NV
  2. 主题A:写V
  3. 主题B:读取V
  4. 主题B:阅读NV
  5. 如果动作按此顺序发生,则线程B将在步骤4中看到NV的更新值。但是,如果在步骤2之后某些内容(包括A)写入NV,则未指定线程B将在步骤4中看到的内容

    一般来说,以这种方式使用挥发物需要深入细致的推理。使用synchronized更容易,更健壮。

    你的例子不清楚:

    • 如果要描述Java程序员必须做什么,那就错了/荒谬。 Java代码无法刷新变量。

    • 如果要在实现级别(例如在JIT编译的代码中)指定必须的内容,那么它也是错误的。

    • 如果想要描述可以在实现级别发生的事情(例如在JIT编译的代码中),那就是正确的。

    我不只是在这里迂腐。编译器可能会认为它不需要刷新线程A中的所有本地非易失性,并且它很可能只会重新加载它在线程B中所需的那些。它如何决定?这是编译器编写者的业务!

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