java - 当涉及的线程之一是 main() 线程时,为什么线程间可见性不需要 volatile 关键字?
问题描述
考虑以下程序:
import java.util.concurrent.TimeUnit;
public class StopThread {
public static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopRequested) {
i++;
System.out.println(i);
}
System.out.println("Stopping the thread!!");
}
};
Thread backgroundThread = new Thread(task);
backgroundThread.start();
TimeUnit.SECONDS.sleep(5);
stopRequested = true;
}
}
- 这里
stopRequested
没有声明为 volatile - 所以理想情况下线程backgroupdThread
不能停止 - 并且无休止地执行 - 但是在本地运行时 - 线程
backgroundThread
优雅地关闭并显示消息:“停止线程!!”。
main() 线程对共享变量的所有更新stopRequested
对其他线程是否可见?即使不使用volatile
关键字?
解决方案
Java 语言规范不保证此结果。
在没有同步操作的情况下(例如volatile
写入和后续读取),写入不会发生在读取之前,因此不能保证可见。
也就是说,读取可能会看到旧值,也可能会看到新值;Java 内存模型允许任一结果。
要查看间隙有多窄,请尝试从循环中删除打印:
while (!stopRequested) {
i++;
}
执行于
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
此代码不会终止。显着的区别是循环体变得不那么复杂,导致 JIT 应用额外的优化:-)
如您所见,不正确同步的程序的行为是不可预测的,并且可能会因最轻微的挑衅而改变。如果你想编写健壮的多线程代码,那么你应该证明你的代码在规范方面是正确的,而不是依赖于测试。
推荐阅读
- powershell - Compress-Archive 正在抛出“System.OutOfMemoryException”
- javascript - 猫鼬中带有 ref 的 ObjectId 数组显示错误 Cast to Array failed for value
- ruby-on-rails - Postgres、Rails 和选择不在组子句中的列
- java - 找到多个名为 [spring_web] 的片段。在非 Maven 项目中
- uml - UML类图设计
- mysql - 当它们没有相同的键时组合来自 2 个表的数据?
- mysql - MySQL 5.7.24 的负载转储后,无法将双精度值正确存储到 MySQL 5.7.25
- postgresql - 跨多个表排除约束?
- python-3.x - 在 Python 3 中导入 H2O 失败
- intellij-idea - Intellij 2018.3 默认自动完成禁用