Multithreading JVM/x86是否保证跨内核的值的非易失性传播?

Multithreading JVM/x86是否保证跨内核的值的非易失性传播?,multithreading,concurrency,jvm,java-memory-model,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; } } 在调用set123时,如果没有将v标记为volatile,它的值是否有可能不会传播到其他内核,即它们的缓存和/或主内存,或者这只是一个时间问题 通过四处询问,总的想法似乎是价值迟早会到达那里,所以只要我们不太在意时间的精确性,就可以让价值不波动,但我想正式

假设以下代码:

class X {
    private int v = 1;
    public void set(int v) { this.v = v; }
    public int get() { return v; }
}
在调用set123时,如果没有将v标记为volatile,它的值是否有可能不会传播到其他内核,即它们的缓存和/或主内存,或者这只是一个时间问题

通过四处询问,总的想法似乎是价值迟早会到达那里,所以只要我们不太在意时间的精确性,就可以让价值不波动,但我想正式确定一下

我的理解是,由于没有获取/发布语义,JMM不能保证这一点,但另一方面,我对缓存一致性/一致性模型的理解有限,TSO-x86最终必然会传播,将其标记为volatile只会设置一道栅栏,禁止CPU存储缓冲区中的重新排序,但除此之外,它最终会传播到其他缓存。关于这一点,只有一点让我想知道——如果另一个内核将某些内容写入同一缓存线中的另一个变量,会发生什么?它在任何情况下都能覆盖v吗

有谁知道这些事情,能给我一个更具体的答案吗

谢谢

根据JVM的特性,在您的示例中没有关系。因此,从形式上讲,不能保证另一个线程会看到共享变量的更新


对我来说,依赖特定JVM和处理器体系结构的实现细节似乎不是一个好主意。今天在实验室有效的东西明天在野外可能会失败。还要注意的是,最终可能需要很长时间,因为它没有上限。事实上,我曾经遇到过这样的情况:由于缺少易失性注释,我的程序似乎被阻塞,不得不重新启动

通过收集特定体系结构的底层实现细节来推断代码的正确性是危险的。对于非易失性访问,JMM不保证线程之间的可见性。仅仅因为它可以工作,并不意味着它总是保证工作

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


如果您想在不使用volatile的情况下保证线程间的可见性,请查看Java9的不透明访问模式

我同意你的第一段。但是,我对x86的理解告诉我,你的第二段花了很长时间,这是不应该发生的。但我可能错了,因此我提出了这个问题@在所有实际情况下,我认为答案都是微弱的“是”。虽然我有过这样的案例,但在实际时间内似乎没有发生。另外,我认为您可以构建一个永远不会发生的情况:编写一个程序,在更新v之后暂停所有其他活动,除了反复尝试读取v。在这种情况下,根本不需要向缓存中传播新值。我假设您的意思是coreA写入v,然后重复读取v,而所有其他内核都不执行与v相关的任何操作,对吗?但是如果是这样的话,那么我真的不在乎。@EnglumeDelysium,是的,这就是我的意思。@EnglumeDelysium当JVM甚至不能保证值会到达缓存时,推理x86体系结构对缓存的保证是没有意义的。例如,它可以将值保留在CPU寄存器中,如果JVM能够证明没有后续操作强制执行写操作,那么写操作可能会被完全消除。对于优化器,只有JMM起作用。底层架构只看到优化器的结果。一旦在x86上执行内存存储指令,所有内核最终都会看到该值。但问题是,当字段是非易失性的时,set和get方法甚至不必执行真正的内存访问。我的意思是,如果JVM发现在同一个线程中没有读取该值,例如,如果在无限循环中调用set方法,那么JVM完全可以消除字段存储。至少一些JVM Excelsior JET在实践中采用了这种优化。问题是,“迟早”是什么意思。当代码调用set123后有一个无限循环时,即使是保守的优化器也可能会将实际的内存写入移到循环后面,因此它并没有消除它,仍然是“晚”而不是“早”只有Chuck Norris可能最终在无限循环结束后感知到写操作…如果有两个变量,它会变得更有趣。假设线程A首先更新v,然后更新w。线程B可以看到其中一个更新而不能看到另一个更新的时间窗口有多大?如果发生这种情况,对你的计划有影响吗?如果线程B看到了第二次更新,但是
不是第一次更新吗?在讨论共享变量之间的关系时,同步问题变得更加紧迫。e、 例如,想象一下,如果线程A更新了一个链接的数据结构,而线程B只看到一些指针发生了变化,会发生什么情况。根据定义,不允许在CPU的存储缓冲区中重新排序在那个CPU上没有这样的事情@真是个奇怪的家伙。任何类型的代码在不施加任何内存可见性约束的情况下,都不会随着时间的推移而取得任何进展。请注意,Thread.sleep…也属于此类,因为它被指定为没有内存可见性效果。