Java 装载屏障真正的作用是什么?
在Java中,当两个线程共享以下变量时:Java 装载屏障真正的作用是什么?,java,concurrency,volatile,Java,Concurrency,Volatile,在Java中,当两个线程共享以下变量时: int a; volatile int b; 如果线程1执行以下操作: a = 5; b = 6; 然后在这两条指令之间插入StoreStore屏障,“a”被刷新回主内存 现在,如果线程2执行以下操作: if(b == 6) a++; 在两者之间插入荷载屏障,我们保证,如果“b”的新值可见,则“a”的新值也可见。但这究竟是如何实现的呢?LoadLoad是否使CPU缓存/寄存器失效?或者只是指示CPU获取变量的值,然后再从CPU读取volatile
int a;
volatile int b;
如果线程1执行以下操作:
a = 5;
b = 6;
然后在这两条指令之间插入StoreStore屏障,“a”被刷新回主内存
现在,如果线程2执行以下操作:
if(b == 6)
a++;
在两者之间插入荷载屏障,我们保证,如果“b”的新值可见,则“a”的新值也可见。但这究竟是如何实现的呢?LoadLoad是否使CPU缓存/寄存器失效?或者只是指示CPU获取变量的值,然后再从CPU读取volatile
我找到了有关Load barrier()的以下信息:
装载障碍物的顺序为:装载1;载荷;Load2确保
Load1的数据在Load2和all访问数据之前加载
随后加载指令。通常,显式加载
在执行推测性加载的处理器上需要屏障
和/或无序处理,其中等待加载指令可以
绕过等待的商店。在保证始终保持
按照负载顺序,屏障相当于无操作
但它并没有真正解释这是如何实现的。我将举一个例子说明这是如何实现的。你可以阅读更多的细节。对于x86处理器,正如您所指出的,LoadLoad最终将成为no-ops。我在文章中指出,马克 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
对于易失性写入:
nop ;*synchronization entry
mov 0x10(%rsi),%rax ;*getfield x
xchg %ax,%ax
movq $0xab,0x10(%rbx)
lock addl $0x0,(%rsp) ;*putfield x
lock
指令是Doug的食谱中列出的存储负载。但是,lock指令也会将所有读取与其他进程同步
锁定指令可用于同步一个用户写入的数据
并由另一个处理器读取
这减少了为易失性负载发出负载存储屏障的开销
尽管如此,我将重申亚西尔亚斯所指出的。它发生的方式对开发人员来说并不重要(如果您对处理器/编译器实现者感兴趣,那就另当别论了)。volatile
关键字是一种界面语句
read b
read a
write a
“缓存”是指代码被重新排序
read a
...
read b
这种重新排序是禁止的。答案取决于处理器体系结构-同一文档中的每个处理器指令都有一个表,该表显示,例如,
LoadLoad
在x86上是不可操作的。那么它是如何工作的呢?我的意思是,在StoreStore之后,这些值被刷新回内存。但是线程2应该如何看到它们呢?如果该加载的计算结果为no op,那么线程2可以继续使用缓存的值。因为处理器的内存模型足够强大,它可以保证情况会如此。我想说的是,Java承诺,如果您使用volatile,某些事情将/不会发生。如何在JVM中实现这一点是特定于处理器的,并使用特殊指令(如果相关,则不使用指令)。您可以在这里阅读关于LoadLoad/x86点的更多信息:您可以进一步阅读LoadLoad不是no-op的CPU体系结构,例如ARM。似乎是沉重的东西,虽然:)不错的链接。我会愚蠢地重新表述:“您将获得由另一个线程写入的最新读取”=>“您最终将在随后读取volatile变量时看到写入—“最终”意味着在实践中几乎立即”;-)实际上,如果您对应用程序运行最快的硬件或如何实现最高性能感兴趣,这一点很重要。我们希望从使用四插槽Xeon(64 SMT)中获得比最终更多的好处。如果您无法控制硬件或仅在单个套接字上运行,这可能不是问题,但并发实现细节以及它们如何影响大型计算机上的可伸缩性,如果在早期就知道它们,肯定会影响设计。@RalfH我是代表普通开发人员使用volatile
关键字说话的。一般来说,开发人员不需要担心实现的易变性。当知道底层架构改变了您将如何使用volatile时,您是否看到过一个实例?:)没有,因为我也不知道硬件是如何实现的,所以我宁愿进行分析。这就是为什么我注意到ConcurrentHashMap中的易失性get-like对性能不好,所以我们最终在内容稳定后用CopyOnWriteHashMaps替换它们。如果你想知道下一步会发生什么,硬件细节是很重要的。我想知道Haswell中的硬件事务性内存对于Java并发性到底意味着什么。好吧,这让人费解:)我不确定我的理解是否正确:因为x86上的加载屏障是不可操作的,所以Marc的程序生成了一个StoreLoad屏障?(这是以“lock”开头的行)。如果volatile读取生成了一个额外的指令(锁),为什么他说我们可以期望volatile读取是免费的?回到我最初的问题,如果我们在Marc的示例中添加了一个非易失性的读取,那么“锁定”指令会起到神奇的作用吗(确保非易失性的可见性)?我实际上指的是CPU缓存中的真正缓存,而不是指令的重新排序。@zhong我认为Janek的担心