JVM / x86 是否保证跨内核的值的非易失性传播?
Does the JVM / x86 guarantee non-volatile propagation of values across cores?
假设以下代码:
class X {
private int v = 1;
public void set(int v) { this.v = v; }
public int get() { return v; }
}
是否有可能通过未将 v
标记为易失性,在调用 set(123)
时其值不会传播到其他核心(即它们的缓存和/或主内存) ,还是这只是时间问题?
问了一圈,总的思路好像是迟早要取值"will get there",所以只要不太在意时间精度,不取值也是可以的不稳定,但我想正式确定。
我的理解是,由于没有获取/释放语义,JMM 不保证它能正常工作,但另一方面,我对缓存一致性/一致性模型(即 TSO-x86)的(有限)理解) 是它最终必然会传播(将其标记为 volatile 只会设置一个栅栏以禁止在 CPU 的存储缓冲区中重新排序,但除此之外它最终会传播到其他缓存)。关于这一点,只有一点让我感到疑惑——如果另一个内核向同一缓存行中的另一个变量写入内容会发生什么?无论如何都可以覆盖v
吗?
哪位懂这方面知识的能给我一个更具体的答案吗?
谢谢!
根据您示例中的 memory model of the JVM there is no happens-before 关系。因此,形式上不保证另一个线程将看到对共享变量的更新。
依赖特定 JVM 和处理器体系结构的实现细节对我来说似乎不是一个好主意。今天在实验室中有效的方法明天可能会在现场失败。另请注意,eventually 可能是一个很长的时间,因为它没有上限。事实上,我遇到过这样的情况,我的程序似乎由于缺少 volatile
注释而被阻止,并且不得不重新启动。
通过收集特定体系结构的底层实现细节来推断代码的正确性是很危险的。 JMM 不会为非易失性访问提供任何跨线程的可见性保证。仅仅因为它可能有效,并不意味着它总能保证有效。
此外,您的分析中缺少的是编译器可能生成的内容。编译器可能会在寄存器中缓存值,可能会产生 "spurious" 不影响单线程程序正确性但会破坏多线程程序的读取或写入,等等。
如果您想在不使用 volatile 的情况下保证跨线程的可见性,请查看 Java 9 的不透明访问模式。
假设以下代码:
class X {
private int v = 1;
public void set(int v) { this.v = v; }
public int get() { return v; }
}
是否有可能通过未将 v
标记为易失性,在调用 set(123)
时其值不会传播到其他核心(即它们的缓存和/或主内存) ,还是这只是时间问题?
问了一圈,总的思路好像是迟早要取值"will get there",所以只要不太在意时间精度,不取值也是可以的不稳定,但我想正式确定。
我的理解是,由于没有获取/释放语义,JMM 不保证它能正常工作,但另一方面,我对缓存一致性/一致性模型(即 TSO-x86)的(有限)理解) 是它最终必然会传播(将其标记为 volatile 只会设置一个栅栏以禁止在 CPU 的存储缓冲区中重新排序,但除此之外它最终会传播到其他缓存)。关于这一点,只有一点让我感到疑惑——如果另一个内核向同一缓存行中的另一个变量写入内容会发生什么?无论如何都可以覆盖v
吗?
哪位懂这方面知识的能给我一个更具体的答案吗?
谢谢!
根据您示例中的 memory model of the JVM there is no happens-before 关系。因此,形式上不保证另一个线程将看到对共享变量的更新。
依赖特定 JVM 和处理器体系结构的实现细节对我来说似乎不是一个好主意。今天在实验室中有效的方法明天可能会在现场失败。另请注意,eventually 可能是一个很长的时间,因为它没有上限。事实上,我遇到过这样的情况,我的程序似乎由于缺少 volatile
注释而被阻止,并且不得不重新启动。
通过收集特定体系结构的底层实现细节来推断代码的正确性是很危险的。 JMM 不会为非易失性访问提供任何跨线程的可见性保证。仅仅因为它可能有效,并不意味着它总能保证有效。
此外,您的分析中缺少的是编译器可能生成的内容。编译器可能会在寄存器中缓存值,可能会产生 "spurious" 不影响单线程程序正确性但会破坏多线程程序的读取或写入,等等。
如果您想在不使用 volatile 的情况下保证跨线程的可见性,请查看 Java 9 的不透明访问模式。