易失性读取和非易失性字段

时间:2017-08-09 23:27:38

标签: java multithreading concurrency volatile memory-barriers

在阅读this questionthis(特别是第二个答案)之后,我对于内存障碍方面的易失性及其语义感到非常困惑。

在上面的例子中,我们写入一个volatile变量,它导致一个mfence,它反过来将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存,使其他缓存行无效。

但是,非易失性字段可以优化并存储在寄存器中吗?那么我们怎样才能确定给定一个易失性变量的写入之前所有状态的变化都是可见的?如果我们改变1000件事怎么办?

4 个答案:

答案 0 :(得分:2)

JMM给出的保证是 - 如果线程1写入一个volatile变量,并且在线程2读取相同的volatile变量之后,则线程2 保证全部在写入volatile变量之前由线程1进行的更改(包括对非易失性变量所做的更改)。这是一个存在的强有力的保证,每个人都同意。

但是,保证仅适用于线程2看到的内容。 你可能还有另一个线程,线程3,它可能看不到线程1设置的非易失性字段的最新值(线程3可能有,并且被允许缓存值对于那些非易失性领域)。只有在线程3读取相同的volatile之后,才能保证看到来自线程1的非易失性写入

答案 1 :(得分:2)

  

在上面的例子中,我们写入一个volatile变量,它导致一个mfence,它反过来将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存......

这是正确的。

  

使其他缓存行无效。

这是不正确的,或者至少是误导性的。写入内存屏障不会使其他缓存行无效。它是在其他处理器中运行的读取存储器屏障,它使每个处理器的高速缓存行无效。内存同步是线程编写和从volatile变量读取的其他线程之间的协作操作。

Java内存模型实际上保证只保证读取变量的相同变量的读取。实际情况是所有内存缓存行在交叉写入内存屏障时刷新,并且当超过读取内存屏障时所有内存缓存行都无效 - 无论访问的变量如何。

  

但是,非易失性字段可以优化并存储在寄存器中吗?那么我们怎样才能确定给定一个易失性变量的写入之前所有状态的变化都是可见的?如果我们改变1000件事怎么办?

根据this documentation(和其他人),内存障碍也会导致编译器生成用于刷新寄存器的代码。引用:

  

...使用barrier()时,编译器必须丢弃当前在任何机器寄存器中缓存的所有内存位置的值。

答案 2 :(得分:0)

易失性变量共享synchronized的可见性功能,但没有原子性功能。这意味着线程将自动查看volatile变量的最新值。

只有在一组有限的情况下才能使用volatile变量而不是lock。对于volatile变量,必须满足以下两个条件才能提供所需的线程安全性:

写入变量不依赖于其当前值。

变量不参与其他变量的不变。

答案 3 :(得分:0)

您可以观看此JMM explanation video以向您明确JMM的工作原理