【发布时间】:2021-11-09 03:17:43
【问题描述】:
让我们保存一下,我有这段代码,它展示了一个线程读取过时的缓存,这会阻止它退出它的 while 循环。
class MyRunnable implements Runnable {
boolean keepGoing = true; // volatile fixes visibility
@Override public void run() {
while ( keepGoing ) {
// synchronized (this) { } // fixes visibility
// Thread.yield(); // fixes visibility
System.out.println(); // fixes visibility
}
}
}
class Example {
public static void main(String[] args) throws InterruptedException{
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
Thread.sleep(100);
myRunnable.keepGoing = false;
}
}
我相信 Java 内存模型保证了对 volatile 变量的所有写入与来自任何线程的所有后续读取同步,从而解决了问题。
如果我的理解是正确的,同步块生成的代码也会清除所有挂起的读取和写入,作为一种“内存屏障”并解决问题。
从实践中我看到插入yield 和println 也会使变量更改对线程可见并且它正确退出。我的问题是:
yield/println/io 是由 JMM 以某种方式保证的内存屏障,还是不能保证有效的幸运副作用?
编辑:至少我在这个问题的措辞中做出的一些假设是错误的,例如关于同步块的假设。我鼓励问题的读者阅读下面发布的答案中的更正。
【问题讨论】:
-
Yield() 放弃运行线程,让其他人运行,所以它会看到变化。 JMM 的目的是对线程的执行设置 时间 约束,以便应用程序在执行中保持一致(JMM),所以我猜测延迟+ println 中的代码会导致您提到的效果.
-
@AliasCartellano JMM 不会提供任何实时约束。正确同步的程序(没有数据竞争的程序)只会给出顺序一致的执行。但是顺序一致性没有任何实时限制;为此,您需要查看线性化。所以简单来说,对于 SC 来说,阅读陈旧的内容是完全可以的;只要不违反 PO。如果你愿意,我可以给你更多细节。
标签: java multithreading caching java-memory-model