如何理解JDK9内存模型?
我正在学习 JDK9 内存模型。
在观看Java Memory Model Unlearning Experience演讲
并阅读
Using JDK 9 Memory Order Modes 一文后。
我对一些概念感到困惑。
-
不透明是否立即保证可见性?
-
如何理解论文中的偏序和全序?
对于第一个问题,论文说
使用等待变量值的空自旋几乎从来都不是一个好主意。使用 Thread.onSpinWait、Thread.yield 和/或阻塞同步来更好地应对“最终”可能需要很长时间的事实,尤其是当系统上的线程数多于内核数时。
所以如果我写代码:
// shared variable I and VarHandle I_HANDLE which referred to I
public static int I = 0;
public static final VarHandle I_HANDLE;
// Thread-1
I_HANDLE.setOpaque(1);
// Thread-2
while((int) I_HANDLE.getOpaque() == 0){
}
线程 2 最终终止,但可能会在很长时间后终止?
如果是这样,是否有任何最小方法可以保证线程 2 立即看到线程 1 的修改?(发布/获取?易变?)
回答
没有像“立即”更新这样的东西。甚至电力也以有限的速度移动。通常,要求在特定时间跨度内可感知的效果就像要求操作的特定执行时间。两者都不能保证,因为它们是 JVM 无法更改的底层架构的属性。
实际上,当然,JVM 开发人员试图使操作尽可能快,而对您而言,作为程序员,最重要的是,关于更新的线程间可见性,没有比不透明写入更快的替代方法。更强的访问模式不会改变更新变得可见的速度,它们为读取和写入的重新排序添加了额外的约束。
因此,在您的示例中,随着体系结构和系统负载允许1 的速度,更新将变得可见,但不要要求实际数字。没有人能说需要多长时间。如果您需要在时间量方面的保证,您需要一个特殊的(“实时”)实现,它可以为您提供 Java 内存模型之外的额外保证。
1举一个实际场景:线程 1 和 2 可能会竞争同一个 CPU。线程 1 写入值并在任务切换之前继续运行操作系统特定的时间(甚至不能保证线程 2 是下一个)。这意味着可能会流逝相当长的时间,无论是挂钟时间还是写入后线程 1 的进度。当然,其他线程也可能同时在其他 CPU 核上取得很大进展。但也有可能在线程 1 提交写入之前线程 2 的轮询是线程 1 没有机会写入新值的原因。这就是为什么你应该用onSpinWait或标记这样的轮询循环yield,让执行环境有机会防止这种情况发生。看到这个问答 讨论两者之间的区别。