JVM / x86是否保证值在内核之间的非易失性传播?

时间:2019-05-06 08:43:17

标签: multithreading concurrency jvm java-memory-model

假设以下代码:

class X {
    private int v = 1;
    public void set(int v) { this.v = v; }
    public int get() { return v; }
}

如果没有将v标记为易失性,则很有可能在调用set(123)时不会将其值传播到其他内核(即它们的缓存和/或主内存),或者这只是时间问题吗?

通过询问,一般的想法似乎是值迟早会“到达那里”,因此,只要我们不在乎时间的准确性,就可以将值保持不变,但我想正式确定。

我的理解是,由于没有获取/发布语义,因此JMM不能保证它能正常工作,但另一方面,我(有限)对缓存一致性/一致性模型(即TSO-x86)的理解是它必须最终传播(将其标记为volatile会简单地设置围栏以禁止对CPU的存储缓冲区进行重新排序,但最终会将其传播到其他缓存)。关于这一点,只有一点让我感到奇怪-如果另一个内核向同一缓存行中的另一个变量写入内容会怎样?在任何情况下都可以覆盖v吗?

对此事有知识的人能给我一个更具体的答案吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

根据JVM的memory model,在您的示例中没有happens-before关系。因此,从形式上来说,没有任何保证可以保证另一个线程永远不会看到共享变量的更新。

依靠特定JVM和处理器体系结构的实现细节对我来说似乎不是一个好主意。今天在实验室中有效的方法明天可能会在现场失效。另请注意,最终可能会很长一段时间,因为没有上限。实际上,我遇到过一些情况,我的程序似乎由于缺少volatile批注而被阻塞,不得不重新启动。

答案 1 :(得分:1)

通过收集特定体系结构的基础实现细节来推断代码的正确性是很危险的。对于非易失性访问,JMM不会对线程进行任何可见性保证。仅仅因为它可能起作用,并不意味着它总是可以保证起作用。

此外,分析中缺少的是编译器可能生成的内容。编译器可能会将值缓存在寄存器中,可能会产生“虚假”读取或写入,这些读取或写入不会影响单线程程序的正确性,但会破坏多线程程序等。

如果您想确保在不使用volatile的情况下跨线程的可见性,请查看Java 9的不透明访问模式。