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

时间:2013-03-12 11:59:16

标签: java concurrency volatile

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

int a;
volatile int b;

如果线程1执行:

a = 5;
b = 6;

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

现在,如果线程2执行:

if(b == 6)
 a++;

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

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

  

LoadLoad Barriers序列:Load1; LoadLoad; Load2确保了这一点   Load1的数据在Load2和所有数据访问之前加载   后续加载指令已加载。一般来说,显式的LoadLoad   执行投机负载的处理器需要障碍   和/或等待加载指令可以进行的无序处理   绕过等候商店。在保证始终保留的处理器上   负载排序,障碍相当于无操作。

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

2 个答案:

答案 0 :(得分:4)

我将举例说明如何实现这一目标。您可以阅读详细信息here。对于x86处理器,如您所示,LoadLoad最终成为无操作。在文章中,我将Mark指出了

  

Doug列出了StoreStore,LoadLoad和LoadStore

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

这是博客的摘录:

以下是为易失性和非易失性读取生成的代码:

nop                       ;*synchronization entry
mov    0x10(%rsi),%rax    ;*getfield x

对于易失性写入:

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

lock指令是Doug's cookbook列出的StoreLoad。但是锁定指令还将所有读取与其他进程同步为listed

  

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

这减少了必须为易失性负载发出LoadLoad LoadStore障碍的开销。

所有这一切,我将重申assylias所说的话。它的发生方式对于开发人员来说应该不重要(如果您对处理器/编译器实现者感兴趣则是另一个故事)。 volatile关键字是一种说明

的界面
  1. 您将获得由其他主题撰写的最新内容
  2. 您不会被JIT编译器优化所灼伤。

答案 1 :(得分:0)

  

如果LoadLoad的计算结果为no-op,则线程2可以继续使用缓存值。

这由食谱中的“可以订购”表格涵盖。

编程顺序是

read b
read a
write a

通过“缓存”,表示代码已重新排序

read a
...
read b

禁止重新排序。