在Effective Java:第66项中,Joshua Bloch举了一个关于生命失败的例子:
// Broken! - How long would you expect this program to run
class StopThread {
private static boolean stopRequested = false;
public static void main(String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
Run Code Online (Sandbox Code Playgroud)
正如约书亚布洛赫所说,这个计划不会终止.但是,如果我改变i++成System.out.println(i++),它成功终止!
我无法弄清楚它是如何发生的!
问题与变量的内存值有关stopRequest.
此变量未定义为volatile.
如果您有两个处理器,则内部线程检查stopRequest从其注册表中获取的值.
主线程改变stopRequest了另一个处理器的注册表中的值.
所以主线程修改了一个值stopRequest但线程只看到了一个永远不会改变的副本.
修改后看一下源代码PrintStream(感谢ΔλЛ的推荐):使用一个System.out.print命令将使用一个显式synchronized块来打印传递给它的值,这将授予该值stopRequest来自主内存而不是来自处理器的注册表.
添加volatile关键字将通知JVM从主存储器中获取值,而不是处理器的注册表,它可以解决问题.
同样使用关键字synchronized将解决此问题,因为在synchronized块中使用的任何变量都将被采用并更新主存储器.
没有内存模型volatile(主线程使用处理器1和显式线程使用处理器2)
Processor 1 Processor 2 Main memory
----------- ----------- -----------
false false false
true false true // After setting
//stopRequest to true
Run Code Online (Sandbox Code Playgroud)
定义stopRequest为volatile所有线程读取其中stopRequest从主内存.
Processor 1 Processor 2 Main memory
----------- ----------- -----------
NA NA false
NA NA true // After setting
//stopRequest to true
Run Code Online (Sandbox Code Playgroud)