【问题标题】: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 的任何后续读取之间引入“发生在之前”的关系(假设没有干预写入)。您可以按如下方式利用它:

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

    如果操作按此顺序发生,那么线程 B 将在第 4 步中看到 NV 的更新值。但是,如果在第 2 步之后有东西(包括 A)写入 NV,则未指定线程 B 在第 4 步将看到什么.

    一般来说,以这种方式使用 volatile 需要深入而仔细的推理。使用synchronized 更简单、更健壮。


    你的例子不清楚:

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

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

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

    我不只是在这里学究气。编译器可能决定它不需要在线程 A 中刷新 all 本地非易失性数据,并且很可能只在线程 B 中重新加载它需要的非易失性数据。它是如何决定的?那是编译器作者的事!


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

    【讨论】:

    • 多么出色的答案,简洁而透彻且易于理解。
    【解决方案2】:

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

    换句话说,从一个线程直到写入v,一旦另一个线程在该写入之后读取了v,所有读取都可以看到该线程。

    我不确定“刷新到 main”是否是理解这一点的必要方式。 Java 内存模型以 happens-beforesynchronizes-with 的形式记录。我建议从这些方面考虑它。从概念上讲,如果某些“刷新”对于 Promise 来说不是必需的,则 JVM 可以省略它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-26
      • 2023-03-21
      • 2020-09-11
      • 2019-01-19
      • 2020-05-11
      • 2012-09-08
      相关资源
      最近更新 更多