java - 为什么没有“volatile”的程序会像“volatile”一样工作?
问题描述
如下所示,该程序有一个共享 var flag
,但没有volatile
:
public class T {
public static void main(String[] args) {
TT jump = new TT(() -> {
while (true) {
if (TT.flag) {
System.out.println("jump");
break;
}
}
});
jump.start();
new TT(() -> {
TT.flag = true; // P1
LocalDateTime t1 = LocalDateTime.now();
while (true) {
if (Duration.between(t1, LocalDateTime.now()).toMillis() > 100) {
break;
}
}
System.out.println("flag");
}).start();
}
static class TT extends Thread {
public static boolean flag = false;
public TT(Runnable o) {
super(o);
}
}
}
程序总是正常返回。所以我相信P1
,flag
设置为的行在其他线程中true
更新。flag
但为什么?flag
不是易变的,为什么它的值会立即更新?总是!
解决方案
但为什么?flag 不是 volatile,为什么它的值会立即更新?总是!
你很幸运;或不幸,取决于你的观点。我在 Ideone 上试过这个,发现它超时而不是正常终止。
请记住:无法观察到并发错误与没有并发错误不同。
对代码最有把握的是,您可以根据规范证明没有错误。这并不意味着代码将正常工作。它只是意味着问题出在JVM实现中。
特别是,您无法证明此代码将正常工作,因为在第二个线程中的写入和第一个线程中的读取之间没有发生之前的关系。flag
添加volatile
创建此保证,因为易失性写入发生在易失性读取之前。
这并不是说没有 volatile 它将永远无法工作,只是不能保证:JVM 只需至少按照规范要求的频率刷新线程的缓存值,但可以更频繁地执行,或者根本不缓存值。
推荐阅读
- python - Selenium 无法获取选择结果
- maven - Maven:如何不依赖父 POM
- c++ - 创建单个线程时 SteamVR 帧峰值
- reactjs - 映射数据的问题 - React
- teradata - 如何在 teradata 中通过 select 语句放置新行?
- javascript - 有没有办法将变量附加到类 javascript
- python - Confusing python variable scope
- javascript - 加载pdf文件,并从代码角度编辑某些值
- vue.js - Vue 测试工具 - 如何通过 ref 查找元素?
- angular - 将 ngOnChange 处理程序添加到整个 Angular 项目