Java 中同步的记忆效应

2024-03-05

JSR-133 常见问题解答 http://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html#synchronization says:

但同步还有更多内容 而不是相互排斥。同步 确保内存由线程写入 在同步块之前或期间 以可预测的方式可见 方式到其他线程 在同一台显示器上同步。后 我们退出同步块,我们 释放监视器,其中有 将缓存刷新到 main 的效果 内存,以便由此进行的写入 线程对其他人可见 线程。在我们可以输入之前 同步块,我们获取 监视器,其作用是 使本地处理器缓存失效 这样变量就会被重新加载 从主存储器。然后我们就能够 查看所有可见的写入 通过之前的版本。

我还记得读到过,在现代 Sun VM 上,无竞争的同步很便宜。我对这个说法有点困惑。考虑如下代码:

class Foo {
    int x = 1;
    int y = 1;
    ..
    synchronized (aLock) {
        x = x + 1;
    }
}

对 x 的更新需要同步,但是获取锁是否也会从缓存中清除 y 的值?我无法想象情况会是这样,因为如果这是真的,像锁条带这样的技术可能没有帮助。或者,JVM 是否可以可靠地分析代码,以确保 y 不会在使用相同锁的另一个同步块中被修改,从而在进入同步块时不会将 y 的值转储到缓存中?


简短的回答是JSR-133 的解释太过分了。这不是一个严重的问题,因为 JSR-133 是一个非规范文档,不属于语言或 JVM 标准的一部分。相反,它只是一份文件,解释了一种可能的策略,即充足的用于实现内存模型,但不是一般情况下必要的。最重要的是,关于“缓存刷新”的评论基本上完全不合适,因为基本上零架构将通过执行任何类型的“缓存刷新”来实现 Java 内存模型(许多架构甚至没有这样的指令)。

Java 内存模型是根据可见性、原子性、发生在关系等方面进行正式定义的,这准确地解释了线程的含义must看看什么,采取什么行动must使用精确(数学)定义的模型在其他操作和其他关系之前发生。未正式定义的行为可能是随机的,或者在某些硬件和 JVM 实现的实践中是明确定义的 - 但当然,您永远不应该依赖于此,因为它将来可能会发生变化,并且您永远无法真正确定它首先是明确定义的,除非您编写 JVM 并且非常了解硬件语义。

因此,您引用的文本并没有正式描述 Java 的保证,而是描述了一些内存排序和可见性非常弱的假设架构如何保证could使用缓存刷新来满足 Java 内存模型要求。任何关于缓存刷新、主内存等的实际讨论显然不适用于 Java,因为这些概念在抽象语言和内存模型规范中不存在。

在实践中,内存模型提供的保证比完全刷新要弱得多——让每个原子、并发相关或锁定操作刷新整个缓存将是非常昂贵的——而这在实践中几乎从未这样做过。相反,使用特殊的原子 CPU 操作,有时与记忆障碍 https://en.wikipedia.org/wiki/Memory_barrier指令,这有助于确保内存可见性和排序。因此,廉价的无竞争同步和“完全刷新缓存”之间的明显不一致可以通过注意到第一个是正确的而第二个不是这样来解决 - Java 内存模型不需要完全刷新(并且在实践中不会发生刷新)。

如果正式的内存模型有点太重而难以消化(您不会孤单),您也可以通过查看更深入地研究这个主题道格·李的食谱 http://gee.cs.oswego.edu/dl/jmm/cookbook.html,它实际上在 JSR-133 FAQ 中链接,但从具体的硬件角度来解决这个问题,因为它是供编译器编写者使用的。在那里,他们确切地讨论了特定操作(包括同步)所需的障碍 - 并且那里讨论的障碍可以很容易地映射到实际硬件。许多实际映射都在说明书中讨论过。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中同步的记忆效应 的相关文章

随机推荐