在阅读了this question和this (特别是第二个答案)之后,我对易失性及其在内存屏障方面的语义感到非常困惑。
在上面的例子中,我们写入一个易失性变量,这会导致一个mfence,它会依次将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存,从而使其他缓存线无效。
然而,非易失性字段可以被优化并存储在寄存器中,例如?那么,我们如何才能确保写入到易失性变量之前的所有状态更改都是可见的呢?如果我们有1000件东西呢?
发布于 2017-08-10 18:54:56
JMM给出的保证是-如果线程1写入易失性变量,并且在线程2读取相同的易失性变量之后,则线程2的被保证在写入易失性变量(包括对非易失性变量所做的更改)之前查看线程1所做的所有更改()。这是一个强有力的保证,每个人都同意。
但是,该保证仅适用于Thread 2看到的内容。您可能还有另一个线程Thread3,它可能看不到由Thread1设置的非易失性字段的最新值(线程3可能具有并被允许缓存这些非易失性字段的值)。只有在线程3读取相同的易失性后,才能保证看到来自线程1的非易失性写入
发布于 2017-08-11 07:47:43
在上面的例子中,我们写一个易失性变量,这会导致一个mfence,然后它会将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存中...
这是正确的。
正在使其他缓存线无效的
。
这是不正确的,或者至少是误导性的。使其它高速缓存线无效的不是写存储器屏障。它是在其他处理器中运行的读内存屏障,它使每个处理器的高速缓存线无效。内存同步是线程写入和从volatile变量读取的其他线程之间的协同操作。
Java内存模型实际上保证了只有对写入的相同变量的读取才能保证更新该变量。实际情况是,在跨越写存储器屏障时,所有存储器高速缓存线都会被刷新,并且当跨越读存储器屏障时,所有存储器高速缓存线都会失效--而不管正在访问的变量是什么。
然而,非易失性字段可以被优化并存储在寄存器中,例如?那么,我们如何才能确保写入到易失性变量之前的所有状态更改都是可见的呢?如果我们改变1000件事呢?
根据this documentation (和其他人)的说法,内存屏障还会导致编译器生成刷新寄存器的代码。引述如下:
...当使用barrier()时,编译器必须丢弃当前缓存在任何机器寄存器中的所有内存位置的值。
发布于 2017-08-11 08:43:36
编译器负责确保内存模型的语义被保留。在您的示例中,编译器将确保在易失性写入之前将任何跨线程可见的值写入内存(在x86上,普通存储就足以达到此目的)。
https://stackoverflow.com/questions/45602130
复制相似问题