Java内存模型 - volatile和x86

时间:2017-04-27 21:31:27

标签: java multithreading cpu volatile java-memory-model

我试图理解java volatile的内在函数及其语义,以及它对底层架构及其指令的转换。如果我们考虑以下博客和资源

fences generated for volatileWhat gets generated for read/write of volatileStack overflow question on fences

这是我收集的内容:

  • volatile read在它之后插入loadStore / LoadLoad障碍(x86上的LFENCE指令)
  • 它可以防止使用后续写入/加载重新排序负载
  • 应该保证加载由其他线程修改的全局状态,即在LFENCE之后,其他线程完成的状态修改对其CPU上的当前线程可见。

我正在努力理解的是: Java不会在x86上发出LFENCE  即读取volatile不会导致LFENCE ....我知道x86的内存排序会阻止使用lods / stored对负载进行重新编写,因此需要注意第二个要点。但是,我假设为了使该状态可以被该线程看到,应该发出LFENCE指令以保证在执行栅栏后的下一条指令之前所有LOAD缓冲区都被耗尽(根据英特尔手册)。我理解x86上有cahce coherence协议,但是volatile读取仍然应该消耗缓冲区中的任何LOAD,不是吗?

2 个答案:

答案 0 :(得分:5)

在x86上,缓冲区固定到缓存行。如果缓存行丢失,则不使用缓冲区中的值。因此,不需要围栏或排出缓冲区;它们包含的值必须是最新的,因为另一个核心无法在不首先使高速缓存行无效的情况下修改数据。

答案 1 :(得分:0)

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

负载具有获取语义

r1=X
[LoadLoad]
[LoadStore]

商店具有发布语义

[LoadStore]
[StoreStore]
Y=r2

如果要先存储然后再加载,则最终结果是:

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

问题在于,加载和存储仍然可以重新排序,因此顺序不一致。这对于Java内存模型是必需的。防止这种情况的唯一方法是使用[StoreLoad]。

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

并且最合乎逻辑的地方是将其添加到写入中,因为通常读取比写入更频繁。这样写就变成:

[LoadStore]
[StoreStore]
Y=r2
[StoreLoad]

由于X86提供了TSO,因此以下防护栏可以禁止操作:

[LoadLoad] [LoadStore] [StoreStore]

因此,唯一相关的是[StoreLoad],这可以通过MFENCElock addl %(RSP),0

完成

LFENCE和SFENCE与这种情况无关。 LFENCE和SFENCE用于弱排序的加载和存储(例如SSE的加载和存储)。

在X86上[StoreLoad]的作用是停止执行加载,直到耗尽存储缓冲区为止。这样可以确保在存储变为全局可见(已离开存储缓冲区并进入L1d)之后,加载在全局范围内可见(因此从内存/缓存中读取)。