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

影响其他非易失性变量内存一致性的Java易失性变量,java,synchronization,shared-memory,volatile,memory-corruption,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+)实际规则是“对可变变量v的写入(§8.3.1.4)与任何线程对v的所有后续读取同步(其中“后续”根据同步顺序定义)。” 换句话说,

场景A

A1。写入易失性变量

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

场景B

B1。从易失变量中读取

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

  • 场景A和场景B是否是与volatile相关的正确行为 变量?或者场景A也包括B2,或者场景B 还包括A2
  • 这些场景是原子的吗?还有别的事吗 在A1和A2之间?还是B1和B2
(使用Java 1.8/1.5+)

实际规则是“对可变变量v的写入(§8.3.1.4)与任何线程对v的所有后续读取同步(其中“后续”根据同步顺序定义)。”

换句话说,从一个线程到写入
v
的写操作,一旦另一个线程在该写操作之后读取了
v
,则该线程的读操作都是可见的

我不确定“冲洗到主管道”是理解这一点的必要方式。Java内存模型是根据发生在之前发生的情况记录的,并与之同步。我建议用这些术语来思考。从概念上讲,JVM可以省略某些“刷新”,如果它们不是承诺所必需的。

实际规则是“对易失性变量v的写入(§8.3.1.4)与任何线程对v的所有后续读取同步(其中“后续”是根据同步顺序定义的)。”

换句话说,从一个线程到写入
v
的写操作,一旦另一个线程在该写操作之后读取了
v
,则该线程的读操作都是可见的


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

写入易失性变量并不保证刷新非易失性变量1。但是,它将在对volatile的写入和对volatile的任何后续读取之间引入一种“发生在”关系(假设没有对其进行中间写入)。您可以通过以下方式利用此漏洞:

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

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


    你的例子不清楚:

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

    • 如果它是一个必须在实现级别(例如在JIT编译的代码中)发生什么的规范,那么它也是错误的

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

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



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

    写入易失性变量并不保证刷新非易失性变量1。但是,它将在对volatile的写入和对volatile的任何后续读取之间引入一种“发生在”关系(假设没有对其进行中间写入)。您可以通过以下方式利用此漏洞:

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

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


    你的例子不清楚:

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

    • 如果它是一个必须在实现级别(例如在JIT编译的代码中)发生什么的规范,那么它也是错误的

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

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



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

    这是一个多么好的答案,简洁却透彻且可理解。这是一个多么好的答案,简洁却透彻且可理解。