我将尝试重现以下有效Java(第二版)中提到的代码。 属于第10章并发项66。
public class StopThread {
private static boolean stopRequested;
public static void main(String args[]) {
Thread backgroudThread = new Thread(new Runnable(){
public void run() {
int i = 0;
while(!stopRequested) {
i ++;
}
}
});
backgroudThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
根据作者的描述,当您运行上述过程段时,backgroupdThread将不会终止。但是我无法在设置Java8环境的机器中重现它。那么这个问题是否已在Java最新版本中进行了优化?
已更新
我在linux操作系统(redhat)中再试一次。它总是不会终止。之前我在Win7操作系统COREi5中运行它。它总是终止。
答案 0 :(得分:3)
嗯,如果要更准确,作者写道:
然而,在我的机器上,程序永远不会终止:背景 线程永远循环!
现在你仍然可以争论这个原因他显然没有“永远”地运行这个程序但是你明白了。
顺便说一句,在我的机器上,程序永远不会终止(JDK 1.7.0_45)
顺便说一句2:如果我们希望它终止,我们所要做的就是将stopRequested
声明为volatile
更新:
为了给没有这本书的人提供更多的上下文(并且真的应该得到它!),作者稍后会解释这个可能发生的原因是由于编译器检测到stopRequested
未在本地更改,因此允许以下优化:
while(!stopRequested) {
i ++;
}
变为:
if (!stopRequested)
while(true) {
i ++;
}
JVM热点执行此类优化(称为提升)。如果你想深入研究为什么是一种合法的优化,你应该做一些阅读:JLS §17.4.3 Programs and Program Order
答案 1 :(得分:2)
不,作者说它永远不会终止在他的机器上。这完全是可能的,而且正是他试图做出的:应该同步对可变数据的共享访问,不仅要确保原子性,还要确保可见性。
虽然分配给boolean
是原子的,但后台线程可能不看主线程更改为stopRequested
。< / p>
作者还提出了一个简单的解决方法:将stopRequested
声明为volatile
。
答案 2 :(得分:1)
根据作者的描述,当您运行上述过程段时,backgroundThread将不会终止。
我没有把这本书放在我面前,但我强烈怀疑这不是那么说的。
我认为它实际上说的是背景线程可能不会终止。单词&#34;不会&#34;和&#34;可能不会&#34;意思不同......
更新 - 请参阅@ alfasin对Bloch实际编写内容的回答。
但我无法在设置Java8环境的机器中重现它。那么这个问题是否已在Java最新版本中进行了优化?
没有。没有&#34;优化&#34;。与众不同。
实际上,该程序已经误入了Java语言规范所指出的行为未指定的区域。特别是,后台线程不会保证将看到主线程对该标志所做的更改。但它可能会看到它......取决于各种各样的东西。
您不能在Java 8上重现这一点并不奇怪。您可能也难以在较旧的Java平台上重现它。从某种意义上说,正是这种困难&#34;这就是这类问题如此粗糙的原因。
答案 3 :(得分:1)
此问题是否已在Java最新版本中进行了优化?
反过来说:这个&#34;问题&#34;精确存在 due 到Java Memory Model允许的优化。这些规定绝对没有从Java 8规范中删除,并且仍然存在。它们在多线程性能方面具有显着优势。
Java提供了特定的语言功能,使您的示例能够正常工作,但它并不强制在任何地方使用这些功能;它让程序员可以自行决定将它们与性能成本一起应用,只有在需要时才能应用它们。
仅供参考,我已经运行了您问题中的确切代码(加上缺少throws
声明),并且该程序可靠地不会在我的OpenJDK 1.8上终止。