使用一个基本的例子来说明我的问题,我有2个几乎相同的代码。
此代码导致while
循环无限运行。
private boolean loadAsset() {
new Thread(new Runnable() {
@Override
public void run() {
// Do something
loaded = true;
}
}).start();
while (!loaded) {
// System.out.println("Not Loaded");
}
System.out.println("Loaded");
return false;
}
然而,这段代码(即在while循环中执行某些操作)会导致成功评估loaded
变量,并允许while
循环中断并完成方法。
private boolean loadAsset() {
new Thread(new Runnable() {
@Override
public void run() {
// Do something
loaded = true;
}
}).start();
while (!loaded) {
System.out.println("Not Loaded");
}
System.out.println("Loaded");
return false;
}
任何人都可以向我解释为什么会这样吗?
答案 0 :(得分:7)
第一个循环仅“出现”无限运行。您实际上正在运行“活动等待”,烧掉100%的CPU,这样您的操作系统或JVM就无法进行上下文切换而让另一个线程运行。
另一方面,System.out.println()
涉及I / O,导致稍微“无效等待”。操作系统或JVM可以切换上下文,另一个线程启动。
如果您运行第一个程序10个小时,我确信循环最终会破坏
答案 1 :(得分:6)
检查'loaded'是否明确声明为volatile。
说明:如果多个线程读取和/或写入变量,则需要采取适当的线程安全性度量。一种这样的线程安全措施是易失性的,它适用于原始值(或对象引用),它们被读取或写为“简单”动作,在给定的场合写入的值不依赖于先前的读取价值。有关更多信息,我在我的网站上有一篇关于volatile的文章(以及有关线程安全的其他信息)可能会有所帮助。
答案 2 :(得分:4)
如果loaded
不是易失性的,JIT可以通过将其放入寄存器而不是每次都从内存加载来自由优化它。在第二种情况下,循环过于复杂,JIT不必假设每次都不需要加载loaded
。
注意:它是JIT而不是优化代码的javac
编译器。
答案 3 :(得分:2)
阅读Memory Consistency Errors。基本上,不同的线程对于应该是相同数据的内容具有不一致的视图。要解决此问题,请阅读Synchronization。或者只需将loaded
声明为volatile
,因为它的值仅由单个线程写入。
答案 4 :(得分:0)
我相信你正在遇到一个Busy Wait空循环,它永远不会睡觉。因此,您设置加载到true的线程永远不会运行。