java - volatile 写入的成本

标签 java multithreading volatile

我研究了 x86 硬件中 Java volatile 写入的成本。 我计划在共享内存位置上使用 Unsafe 的 putLongVolatile 方法。查看实现,putLongVolatile get 已转换为 Link 中的 Unsafe_SetLongVolatile然后进入 AtomicWrite,然后是栅栏 Link

简而言之,每个 volatile 写入都会转换为原子写入,然后是完整的栅栏(mfence 或 x86 中的锁定添加指令)。

问题:

1) 为什么 x86 需要 fence() ?由于商店排序,简单的编译器屏障还不够吗?完整的围栏看起来非常昂贵。

2) putLong 代替 Unsafe 的 putLongVolatile 是更好的选择吗?它在多线程情况下能很好地工作吗?

最佳答案

问题 1 的回答:

如果没有完整的栅栏,您就无法获得 JMM 所需的顺序一致性。

所以X86提供了TSO。因此,您可以免费获得以下障碍[LoadLoad][LoadStore][StoreStore]。唯一缺少的是 [StoreLoad]。

加载具有获取语义

r1=X
[LoadLoad]
[LoadStore]

存储具有发布语义

[LoadStore]
[StoreStore]
Y=r2

如果你先存储然后加载,你最终会得到这样的结果:

[LoadStore]
[StoreStore]
Y=r2
r1=X
[LoadLoad]
[LoadStore]

问题是加载和存储仍然可以重新排序,因此它不是顺序一致的;这对于 Java 内存模型是强制性的。他们防止这种情况的唯一方法是使用 [StoreLoad]。最合乎逻辑的地方是将其添加到写入中,因为通常读取比写入更频繁。

这可以通过 MFENCElock addl %(RSP),0

来完成

问题 2 的回答:

putLong 的问题在于,不仅 CPU 可以对指令重新排序,编译器也可能以导致指令重新排序的方式更改代码。

示例:如果您要在循环中执行 putLong,编译器可能会决定将写入从循环中拉出,并且该值将不会对其他线程可见。如果您想要一个低开销的单写入器性能计数器,您可能需要看看 putLongRelease/putLongOrdered(oldname)。这将阻止编译器执行上述技巧。以及您免费获得的 X86 上的发布语义。

但是很难为你的第二个问题提供一个万能的解决方案,因为这取决于你的目标是什么。

关于java - volatile 写入的成本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43460725/

相关文章:

objective-c - iOS 线程 - 回调最佳实践

java - 在 while 循环中等待(长时间超时)?

c - Linux 中的 C 线程同步

c++ - MS 特定的 volatile 是否会阻止硬件指令重新排序

java - 在java中同时访问 volatile 变量

java - Android Studio 在尝试访问导入库中的方法时显示 "Cannot resolve symbol"

java - LibGDX Ashley 单独渲染和更新

java - 通过 java 程序执行命令时如何为命令提示符编写/提供输入

java - 如何在 Playframework 2.5 上启动并定期调用函数

c - 隐式声明所有变量 volatile