LoadLoad屏障真正起作用了什么?

Jan*_*nek 11 java concurrency volatile

在Java中,当我们有两个线程共享以下变量时:

int a;
volatile int b;
Run Code Online (Sandbox Code Playgroud)

如果线程1执行:

a = 5;
b = 6;
Run Code Online (Sandbox Code Playgroud)

然后在这两条指令之间插入StoreStore屏障,并将'a'刷新回主存储器.

现在,如果线程2:

if(b == 6)
 a++;
Run Code Online (Sandbox Code Playgroud)

在它之间插入一个LoadLoad屏障,我们保证如果'b'的新值可见,那么'a'的新值也是可见的.但实际上这是如何实现的呢?LoadLoad是否使CPU高速缓存/寄存器无效?或者只是指示CPU从CPU再次获取从volatile中读取的变量值?

我找到了有关LoadLoad barrier的信息(http://gee.cs.oswego.edu/dl/jmm/cookbook.html):

LoadLoad Barriers序列:Load1; LoadLoad; Load2确保在Load2访问数据和加载所有后续加载指令之前加载Load1的数据.通常,在执行推测性加载和/或无序处理的处理器上需要显式的LoadLoad障碍,其中等待加载指令可以绕过等待的存储.在保证始终保持负载排序的处理器上,障碍相当于无操作.

但它并没有真正解释如何实现这一目标.

Joh*_*int 4

我将举一个例子来说明如何实现这一目标。您可以在此处阅读更多详细信息。对于 x86 处理器,正如您所指出的,LoadLoad 最终是无操作的。在我链接的文章中,马克指出

Doug 列出了 StoreStore、LoadLoad 和 LoadStore

因此本质上唯一需要的障碍是 x86 架构的 StoreLoad。那么,这是如何在低水平上实现的呢?

这是博客的摘录:

这是它为易失性和非易失性读取生成的代码:

nop                       ;*synchronization entry
mov    0x10(%rsi),%rax    ;*getfield x
Run Code Online (Sandbox Code Playgroud)

对于易失性写入:

xchg   %ax,%ax
movq   $0xab,0x10(%rbx)
lock addl $0x0,(%rsp)     ;*putfield x
Run Code Online (Sandbox Code Playgroud)

lock指令是 Doug 食谱中列出的 StoreLoad。但锁定指令还会将所有读取与列出的其他进程同步

锁定指令可用于同步一个处理器写入的数据和另一处理器读取的数据。

这减少了为易失性加载发出 LoadLoad LoadStore 屏障的开销。

话虽如此,我将重申阿西利亚斯所指出的内容。它发生的方式对于开发人员来说并不重要(如果您对处理器/编译器实现者感兴趣,那就是另一个故事了)。关键字volatile是一种界面说

  1. 您将获得由另一个线程编写的最新读物
  2. 您不会因 JIT 编译器优化而苦恼。