为什么Java没有看到来自另一个线程的更新值?

请查看此代码(摘自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在到达循环时缓存了自己的stopReq副本? 所以它永远不会看到来自其他线程的更新值?

我理解这个问题的解决方案是同步或volatile变量,但我很好奇为什么这个当前的实现不起作用。

谢谢

你的解释是对的

编译器检测到stopReq永远不会在循环中被修改,因为它不是volatile ,所以将while(!stopReq)指令优化为while(true)

即使该值稍后更改,该线程甚至不再读取它。

您应该阅读有关Java内存模型的更多信息,以便更好地理解所有含义。

简而言之stopReq变量不是易失性的或包含在同步块中,这使得VM可以自由地使用优化的本地存储(例如,寄存器等),这些存储不能保证在线程之间立即传播变化。

当您将变量声明为volatile时 ,VM将确保在每个变量写入后插入“内存写入屏障” ,这将强制所有本地更改溢出到实际内存位置,从而使其对所有其他线程可见(在同步块的末尾放置相同的屏障,例如。)

将stopReq设为true,然后停止。 您再次将stopReq设置为false,因为while循环条件始终为true且处于无限循环中。

我测试了这个,不,变量是相同的。 这个例子也为我编译。

错误在这里:

你的while循环继续,只要!stopReq为true,这意味着stopReq为false。 1秒后你将stopReq设置为false – 这没有任何改变。 如果将其设置为true,则!stopReq将变为false,并且循环将结束。

为了非常具体地了解您的查询,为了充分利用现代多处理器硬件的性能,在没有同步的情况下,JVM允许编译器重新排序操作并缓存寄存器和处理器特定高速缓存中的值。 由于主线程在没有同步的情况下写入stopReq,因此由于重新排序和缓存,BTW线程可能永远不会看到写入的值并永远循环。

当您使用同步或易失性时,它们保证可见性并强制编译器不缓存并刷新对主内存的更改。