JSR-133烹饪书如何实施Java内存模型的所有保证

时间:2016-07-16 19:27:44

标签: java multithreading concurrency cpu cpu-cache

我的理解是,JSR-133 cookbook是一个很好的引用指南,介绍了如何使用一系列内存障碍(或至少是可见性保证)来实现Java内存模型。

基于对不同类型障碍的描述也是我的理解,StoreLoad是唯一一个保证所有CPU缓冲区都被刷新到缓存并因此确保新读取(通过避免存储转发)并保证观察到由于缓存一致性而产生的最新值。

我正在查看易失性/常规商店/负载的不同程序顺序交叉所需的特定障碍表以及需要哪些内存障碍。

从我的直觉来看,这张桌子似乎不完整。例如,Java内存模型保证监视器的获取操作对其在另一个线程中释放之前执行的所有操作的可见性,即使更新的值是非易失性的。在上面链接的表中,似乎只有刷新CPU缓冲区并传播更改/允许观察到新更改的唯一操作是Volatile Store或MonitorExit,后跟Volatile Load或MonitorEnter。我不知道这些障碍如何在上面的例子中保证可见性,当这些操作(根据表格)仅使用LoadStore和StoreStore时,我的理解仅涉及在线程中重新排序而不能强制执行在保证之前发生(跨线程)。

我的理解在哪里出错了?或者此实现仅在执行之前执行,而不是同步保证或获取/释放监视器上的额外操作。

由于

3 个答案:

答案 0 :(得分:1)

文档中的障碍是抽象概念,或多或少地映射到不同CPU上的不同事物。但它们只是指导方针。 JVM实际必须遵循的规则是JLS第17章中的规则。

作为一个概念的障碍也是“全球性的”,因为他们在之前和之后的指令中命令所有

  

例如,Java内存模型保证监视器获取操作的可见性,以及在另一个线程中释放之前执行的所有操作,即使更新的值是非易失性的。

获取监视器的是食谱中的 monitor-enter ,只需要对其他争用锁定的线程可见。 monitor-exit 是释放操作,它将阻止它之前的加载和存储移动。您可以在cookbook表中看到这一点,其中第一个操作是正常加载/存储,第二个操作是volatile-store或monitor-exit。

在具有总存储顺序的CPU上,存储缓冲区(如果可用)对正确性没有影响;只是表现。

在任何情况下,JVM都可以使用提供JLS所需的原子性和可见性语义的指令。这是关键要点:如果您编写Java代码,则需要针对JLS中定义的抽象机进行编码。您只会深入了解具体机器的实现细节,如果只编码抽象机器不能提供您所需的性能。你不需要去那里是为了正确。

答案 1 :(得分:1)

  

StoreLoad是唯一一个保证所有CPU缓冲区都被刷新到缓存并因此确保新读取(通过避免存储转发)并保证由于缓存一致性而观察最新值的方法。

对于x86架构可能也是如此,但您不应该考虑该抽象级别。情况可能是高速缓存一致性对于处理器执行来说可能是昂贵的。

以移动设备为例,一个重要目标是减少耗电量的电池使用量。在这种情况下,他们可能不参与缓存一致性,StoreLoad会丢失此功能。

  

我不知道这些障碍如何保证我上面例子中的可见性,当这些操作(根据表格)仅使用LoadStore和StoreStore时,我的理解仅涉及在线程中重新排序和无法在保证之前强制执行(跨线程)。

让我们考虑一个不稳定的领域。易变的负载和存储看起来如何?好吧,Aleksey Shipilëv has a great write up on this,但我会拿一块。

易失性存储然后后续加载看起来像:

<other ops>
[StoreStore]
[LoadStore]
x = 1; // volatile store
[StoreLoad] // Case (a): Guard after volatile stores

...

[StoreLoad] // Case (b): Guard before volatile loads
int t = x; // volatile load
[LoadLoad]
[LoadStore]
<other ops>

因此,<other ops>可以是非易失性写入,但正如您所见,这些写入在易失性存储之前被提交到内存。然后,当我们准备好阅读时,LoadLoad LoadStore会强制等待volatile store成功。

最后,StoreLoad之前和之后确保易变的加载和存储如果紧接在彼此之前就不能重新排序。

答案 2 :(得分:0)

我不确定你在哪里StoreLoad障碍是唯一强制执行某些特定行为的类型。抽象地说,所有障碍都强制执行它们被强制执行的内容。例如,LoadLoad可防止任何先前的加载与任何后续加载重新排序。

可能存在体系结构特定描述如何实施特定屏障:例如,在x86上,除了 StoreLoad之外的所有障碍都是无操作的,因为芯片架构自动强制执行其他排序,StoreLoad通常实现为存储缓冲区刷新。尽管如此,所有障碍都有其抽象的定义,这些定义与架构无关,并且根据这一定义定义了食谱,同时将概念障碍映射到实际的ISA特定实现。

特别是,即使某个特定平台上的屏障是“无操作”,也意味着保留了排序,因此满足了所有发生之前和其他同步要求。