Java volatile是否阻止缓存或强制执行直写缓存?

Java volatile是否阻止缓存或强制执行直写缓存?,java,multithreading,caching,concurrency,volatile,Java,Multithreading,Caching,Concurrency,Volatile,我试图理解Java的volatile关键字,它与在具有CPU缓存的多线程程序中写入一个volatile原子变量有关 我已经阅读了一些教程和Java语言规范,尤其是。我的理解是,当一个线程将一个新值写入一个可变变量时,更新后的值必须对读取该变量的其他线程可见。对我来说,这些语义可以通过以下两种方式之一实现: 线程可以在CPU缓存中缓存易失性变量,但必须立即将对缓存中变量的写入刷新到主内存。换言之,缓存是可用的 线程永远不能缓存易失性变量,必须在主内存中读写此类变量 本文()中提到了方法1,它说:

我试图理解Java的
volatile
关键字,它与在具有CPU缓存的多线程程序中写入一个volatile原子变量有关

我已经阅读了一些教程和Java语言规范,尤其是。我的理解是,当一个线程将一个新值写入一个可变变量时,更新后的值必须对读取该变量的其他线程可见。对我来说,这些语义可以通过以下两种方式之一实现:

  • 线程可以在CPU缓存中缓存易失性变量,但必须立即将对缓存中变量的写入刷新到主内存。换言之,缓存是可用的

  • 线程永远不能缓存易失性变量,必须在主内存中读写此类变量

  • 本文()中提到了方法1,它说:

    通过声明计数器变量volatile,所有写入计数器的操作 变量将立即写回主内存

    方法2在Stackoverflow问题“”中提到,该问题还表示:

    此变量的值永远不会在本地线程中缓存:all 读写操作将直接进入“主内存”

    哪种方法是Java中使用的正确方法?

    相关的堆栈溢出问题,不回答我的问题:


    保证仅限于您在语言规范中看到的内容。从理论上讲,写入易失性变量可能会强制缓存刷新到主内存,也可能不会,可能是后续读取强制缓存刷新,或者在没有缓存刷新的情况下导致缓存之间的数据传输。这种模糊性是故意的,因为它允许未来可能的优化,如果更详细地阐述易变变量的机制,这可能是不可能的


    实际上,对于当前的硬件,这可能意味着,如果没有一致的缓存,写入易失性变量将强制缓存刷新到主内存。当然,对于一致性缓存,不需要这样的刷新。

    在Java中,最准确的说法是,所有线程都将看到对易失性字段的最新写入,以及在该易失性读/写之前的任何写入

    在Java抽象中,这在功能上等同于从共享内存中读取/写入易失性字段(但在较低级别上这并不严格准确)


    与Java相关的级别要低得多;在现代硬件中,任何和所有任何和所有内存地址的读取/写入总是发生在L1和寄存器中。也就是说,Java的设计目的是对程序员隐藏这种低级行为,因此这只是概念上与讨论相关

    当我们在Java中的字段上使用
    volatile
    关键字时,这只是告诉编译器在对该字段的读/写操作中插入一些称为内存屏障的内容。记忆障碍有效地保证了两件事

  • 任何读取此地址的线程都将使用最新的值(屏障使它们等待,直到最近一次写入使其返回到共享内存,并且在更新后的值使其返回到一级缓存之前,任何读取线程都不能继续)

  • 对任何字段的读/写都不能跨越屏障(也就是说,它们总是在另一个线程继续之前写回,并且编译器/OOO不能将它们移动到屏障之后的某个点)

  • 给出一个简单的Java示例

    //on one thread
    counter += 1; //normal int field
    flag = true; //flag is volatile
    
    //on another thread
    if (flag) foo(counter); //will see the incremented value
    
    本质上,当将
    标志
    设置为
    时,我们会创建一个内存屏障。当线程#2尝试读取此字段时,它会遇到我们的障碍,并等待新值到达。同时,CPU确保在新值到达之前回写
    计数器+=1
    。因此,如果
    flag==true
    ,则
    计数器将递增


    综上所述,

  • 所有线程都可以看到易失性字段的最新值(可以粗略地描述为“读/写通过共享内存”)

  • 对易失性字段的读取/写入在与一个线程上的任何字段的先前读取/写入建立关系之前发生


  • 也不它在赋值点强制内存隔离,这意味着对volatile变量的写入以及之前的所有写入都对其他线程可见,如果这些线程首先读取相同的volatile变量。如果没有读到相同的volatile,则无法保证效果。因此,您在“方法2”中引用的答案似乎是正确的:“Java中的volatile variable”看起来是正确的(晚餐继续,但我只是快速阅读了一下)。您在“方法2”中链接到的教程似乎是垃圾。谢谢。所以这个答案是错误的,对吗?”声明一个易失性Java变量意味着:这个变量的值永远不会被线程本地缓存:所有读写操作都将直接进入“主内存”。“我想你的答案是说可以缓存易失性变量。嗯,我对“写回主内存”有问题“这是其中的一部分,@KookieMonster。我想Warren说的是,在缓存一致性架构中,挥发物可能不会直接写入主内存(我也同意)。事实上,我也有同样的问题,所以我问了Intel,“相干通信通过QPI链路传输,QPI链路使用QPI带宽的一小部分。”因此,实际上并不总是涉及主内存@怪兽你看那意味着你错了,对吧?不需要涉及主内存,如果涉及主内存,则现代CPU的速度会慢得多,因为主内存通常比内核之间的互连慢一个数量级。缓存不会被“刷新”。在易失性写入的情况下发生的主要事情是CPU停止执行加载,直到存储缓冲区耗尽。如果没有