在 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;
}
}
正如约书亚布洛赫所说,这个计划不会终止。
但是,如果我将i++
更改为System.out.println(i++)
,则会成功终止!
我无法弄清楚它是如何发生的!
答案 0 :(得分:5)
问题与变量stopRequest
的内存值有关。
此变量未定义为volatile
。
如果您有两个处理器,则内部线程会检查从其注册表中获取的stopRequest
的值。
主线程改变了另一个处理器的注册表中stopRequest
的值。
因此主线程修改了stopRequest
的值,但线程只看到了一个永不改变的副本。
在查看PrintStream
的源代码后修改(感谢ΔλЛ的推荐):使用System.out.print
命令将使用显式synchronized
阻止打印传递给它的值,这将授予stopRequest
的值来自主存储器,而不是来自处理器的注册表。
添加volatile
关键字将通知JVM从主内存中获取值,而不是处理器的注册表,它可以解决问题。
同样使用关键字synchronized
将解决此问题,因为在同步块中使用的任何变量都将被采用并更新主存储器。
没有volatile
的内存模型(主线程使用处理器1,显式线程使用处理器2)
Processor 1 Processor 2 Main memory
----------- ----------- -----------
false false false
true false true // After setting
//stopRequest to true
将stopRequest
定义为volatile
,其中所有线程都从主内存中读取stopRequest
。
Processor 1 Processor 2 Main memory
----------- ----------- -----------
NA NA false
NA NA true // After setting
//stopRequest to true
答案 1 :(得分:1)
tl; dr:这很可能是println
同步的“意外”副作用。
首先,要意识到线程不是保证不完成;这是因为它不能保证将完成。换句话说,stopRequested
上的竞争条件 - 由一个线程正在写入它,另一个线程正在从中读取,并且两个线程之间没有同步 - 这意味着JVM 可能,但不是必须的,让读者看看作者做了什么。
那么,为什么System.out.println
会改变这个?因为它是一种同步方法。就JLS而言,这实际上并没有为stopRequested
提供任何保证,但它确实意味着JVM必须做一些事情,例如获取(n无关)锁定和建立(无关)发生在边缘之前,这使得更有可能跨线程看到对stopRequested
的写入。
答案 2 :(得分:1)
由于您没有告诉线程stopRequested
是一个可以从该线程外部修改的值,因此无法保证while
将评估该变量的最新值。< / p>
这就是volatile
关键字在这种情况下很有用的原因,因为它会明确地强制执行stopRequested
值,当读取时,它将是任何线程设置的最新值。
还有一些考虑因素,实际上从线程的角度来看,stopRequested
是一个循环不变量,因为它永远不会只被读取,所以也应该考虑优化选择:如果一个值被认为是不进行修改,然后没有理由在每次迭代时对其进行评估。
答案 3 :(得分:0)
System.out.println()
请求在控制台上写入资源,这本身就是一种阻塞方法......这意味着它会阻止控制台上的backgroundThread()
到print()
。这类似于向它发送中断。
因此,backgroundThread()
将意识到布尔值的变化并停止执行,从而终止守护进程。