请查看此代码(摘自Effective Java book)
import java.util.concurrent.TimeUnit;
public class Main {
private static boolean stopReq;
public static void main(String[] args) throws InterruptedException {
Thread bgw = new Thread(new Runnable()
{
public void run(){
int i = 0;
while(!stopReq){ i++;}
}
});
bgw.start();
TimeUnit.SECONDS.sleep(1);
stopReq = true;
}
}
为什么bgw
线程陷入无限循环?它是否在到达循环时缓存了它自己的stopReq
副本?所以它永远不会看到来自其他线程的更新值?
我理解这个问题的解决方案是同步或变量,但我很好奇为什么当前的实现不起作用。
感谢
答案 0 :(得分:12)
您的解释正确。
编译器检测到永远不会在循环中修改stopReq
并且因为它不是volatile
,所以将while(!stopReq)
指令优化为while(true)
。
即使该值稍后更改,该线程甚至不再读取它。
答案 1 :(得分:1)
您应该阅读有关Java Memory Model的更多信息,以便更好地了解所有影响。
很快, stopReq 变量不是易失性的或包含在同步块中,这使得VM可以自由地使用优化的本地存储(例如寄存器等),这些存储不能保证立即传播更改。线程。
当您将变量声明为 volatile 时,VM将确保在每个变量之后写入“内存写屏障”,这将强制所有本地更改被溢出到真实的内存位置,从而使其对所有其他线程可见(同一个屏障位于同步块的末尾,例如。)
答案 2 :(得分:0)
我测试了这个,不,变量是相同的。这个例子也为我编译。
错误在于:
你的while循环继续,只要!stopReq为true,这意味着stopReq为false。 1秒后你将stopReq设置为false - 这没有任何改变。如果将其设置为true,则!stopReq将变为false,并且循环将结束。
答案 3 :(得分:0)
将stopReq设为true,然后停止。您再次将stopReq设置为false,因为while循环条件始终为true且处于无限循环状态。
答案 4 :(得分:0)
要非常具体地了解您的查询,要充分利用现代多处理器硬件的性能,在没有同步的情况下,JVM允许编译器重新排序操作并缓存寄存器和处理器特定高速缓存中的值。 由于主线程在没有同步的情况下写入stopReq,因此重新排序和缓存BTW线程可能永远不会看到写入的值并永远循环。
当您使用同步或volatile时,它们保证可见性并强制编译器不缓存并刷新对主内存的更改。