易失写入的成本

时间:2017-04-17 22:34:56

标签: java multithreading volatile

我在x86硬件中研究了Java中易失性写入的成本。 我打算在共享内存位置使用Unsafe的putLongVolatile方法。查看实现,将putLongVolatile get转换为Link中的Unsafe_SetLongVolatile,然后转换为AtomicWrite,然后是fence Link

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

问题:

1)为什么x86需要fence()?由于商店店铺排序,不是一个简单的编译器障碍吗?完整的栅栏似乎非常昂贵。

2)PutLong代替putLongVolatile of Unsafe是一个更好的选择吗?它会在多线程情况下运行良好吗?

1 个答案:

答案 0 :(得分:2)

问题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上的发布语义。

但是要给第二个问题一个合适的解决方案非常困难,因为这取决于您的目标。

相关问题