了解上下文切换期间的 Java 内存模型
Understanding Java memory model during context switches
最近在一次会议中使用了以下示例来演示多线程环境中的Java内存模型。
public class A {
public static boolean done;
public static void main(String[] args) throws InterruptedException {
done = false;
new Thread(new Runnable(){
public void run() {
System.out.println("running...");
int count = 0;
while (!done) {
count++;
}
System.out.println("Exiting thread");
}
}).start();
System.out.println("in main...");
Thread.sleep(2000);
System.out.println("setting done to true");
done = true;
}
}
我知道上面代码中创建的新线程永远不会退出,因为 done
变量被缓存在线程的本地缓存中。正确的解决方案是使 done
变量可变。
但是如果在while循环里面,我们调用Thread.sleep()
如下
while (!done) {
count++;
try {Thread.sleep(0);} catch(Exception e){}
}
然后线程成功退出。
我的理解是,由于 sleep(0)
会发生上下文切换,使缓存条目无效,因此每次检索 done
的更新值。我的理解正确吗?此行为是否也取决于机器的内核数量?
Java 语言规范 明确指出 Thread::sleep
没有任何 happens-before 语义(和这些是你唯一想与之推理的人):
... Thread.sleep nor Thread.yield have any synchronization semantics...
因此,规范不保证您“碰巧”看到使用它的任何输出。你唯一的保证是完成后是volatile
,因为JLS给你这样的保证。
您关于正确同步程序的推理必须支持 happens-before,上下文切换、缓存等无关紧要。
最近在一次会议中使用了以下示例来演示多线程环境中的Java内存模型。
public class A {
public static boolean done;
public static void main(String[] args) throws InterruptedException {
done = false;
new Thread(new Runnable(){
public void run() {
System.out.println("running...");
int count = 0;
while (!done) {
count++;
}
System.out.println("Exiting thread");
}
}).start();
System.out.println("in main...");
Thread.sleep(2000);
System.out.println("setting done to true");
done = true;
}
}
我知道上面代码中创建的新线程永远不会退出,因为 done
变量被缓存在线程的本地缓存中。正确的解决方案是使 done
变量可变。
但是如果在while循环里面,我们调用Thread.sleep()
如下
while (!done) {
count++;
try {Thread.sleep(0);} catch(Exception e){}
}
然后线程成功退出。
我的理解是,由于 sleep(0)
会发生上下文切换,使缓存条目无效,因此每次检索 done
的更新值。我的理解正确吗?此行为是否也取决于机器的内核数量?
Java 语言规范 明确指出 Thread::sleep
没有任何 happens-before 语义(和这些是你唯一想与之推理的人):
... Thread.sleep nor Thread.yield have any synchronization semantics...
因此,规范不保证您“碰巧”看到使用它的任何输出。你唯一的保证是完成后是volatile
,因为JLS给你这样的保证。
您关于正确同步程序的推理必须支持 happens-before,上下文切换、缓存等无关紧要。