3

我对一个线程正在等待while循环中变量更改的情况感兴趣:

while (myFlag == false) {
    // do smth
}

它重复了无数次。

与此同时,另一个线程改变了这个变量的值:

myFlag = true;

如果这个变量不是易失性的,读者线程可以看到改变另一个线程中变量值的结果吗?一般来说,据我所知,它永远不会发生。还是我错了?那么什么时候,什么情况下,第一个线程可以看到变量的变化并退出循环呢?这可能不使用volatile关键字吗?处理器缓存的大小在这种情况下是否起作用?

请解释并帮助我理解!先感谢您!!

4

2 回答 2

8

如果该变量不是易失性的,读者线程能否看到在另一个线程中更改变量值的结果?

也许能够,是的。只是它肯定不会看到变化。

一般来说,据我所知,它永远不会发生。

不,事实并非如此。

您正在写入一个变量,然后在另一个线程中读取它。您是否看到它取决于所涉及的确切处理器和内存架构。如果不涉及任何内存屏障,则不能保证您会看到新值 - 但当然也不能保证您也不会看到它。

于 2013-09-30T21:26:58.157 回答
4

如果该变量不是易失性的,读者线程能否看到在另一个线程中更改变量值的结果?

我想对@Jon 的出色回答进行一些扩展。

Java 内存模型表示,如果特定线程中的所有内存跨越任何内存屏障,它将被更新。读屏障导致特定线程中的所有缓存内存从中央内存更新,写屏障导致本地线程更改写入中央内存。

因此,如果您的线程写入另一个volatile字段或进入一个synchronized块,它将导致您的标志在中央内存中更新。如果读取线程在更新发生后从另一个volatile字段读取或进入部分synchronized中的块// do smth,它将看到更新。你不能依赖什么时候会发生,或者写/读的顺序是否正确发生。如果您的线程没有其他内存同步点,那么它可能永远不会发生。

编辑:

鉴于下面的讨论,我现在已经在各种不同的问题中进行过几次讨论,我想我可能会更多地扩展我的答案。Java 语言及其内存模型提供的保证与 JVM 实现的实际情况之间存在很大差异。JLS 和 JMM 定义了内存屏障,并讨论了仅在同一字段volatile上的读取和写入之间以及同一对象上的锁定之间的“发生前”保证。synchronized

但是,在我听说过的所有体系结构中,强制内存同步的内存屏障的实现并不是特定于字段或对象的。当在一个volatile字段上完成读取并且在特定线程上跨越读取障碍时,它将使用所有中央内存进行更新,而不仅仅是相关的特定volatile字段。volatile写入也是如此。对volatile字段进行写入后,来自本地线程的所有更新都将写入中央内存,而不仅仅是字段。JLS所做的保证是指令不能通过volatile访问重新排序。

因此,如果线程 A 已写入一个volatile字段,那么所有更新,即使是那些标记为的更新volatile都将被写入中央存储器。此操作完成后,如果线程 B 随后从不同的volatile字段读取,他将看到线程 A 的所有更新,甚至那些未标记为 的更新volatile。同样,无法保证这些事件的时间,但如果它们按此顺序发生,则两个线程将被更新。

于 2013-09-30T21:43:48.403 回答