通过不同的线程同时读取和更改变量

时间:2013-09-30 21:25:24

标签: java multithreading concurrency thread-safety thread-synchronization

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

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

重复无数次。

与此同时,另一个帖子已更改此变量的值:

myFlag = true;

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

请解释并帮助我理解!提前谢谢!!

2 个答案:

答案 0 :(得分:8)

  

如果此变量不是易失性的,那么读者线程是否可以看到更改另一个线程中变量值的结果?

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

  

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

不,事实并非如此。

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

答案 1 :(得分:4)

  

如果此变量不是易失性的,那么读者线程是否可以看到更改另一个线程中变量值的结果?

我想对@ Jon的优秀答案进行一些扩展。

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

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

修改

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

但是,在我听说的所有体系结构中,强制执行内存同步的内存屏障的实现是不是字段或对象特定的。当在volatile字段上进行读取并且在特定线程上跨越读取障碍时,将使用所有中央内存更新它,而不仅仅是特定{{1}有问题的字段。 volatile次写入也是如此。在对volatile字段进行写入后,将本地线程的所有更新写入中央内存,而不仅仅是字段。 JLS 所做的保证的是,指令无法在volatile访问权限之后重新排序。

因此,如果线程A已写入volatile字段,那么所有更新,即使那些标记为volatile的更新也将写入中央内存。完成此操作后,如果thread-B从另一个volatile字段读取,则他将看到所有线程A的更新,即使那些未标记为volatile的更新。同样,围绕这些事件的时间有 no 保证,但如果它们以该顺序发生,则两个线程将被更新。