Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 通过不同线程同时读取和更改变量_Java_Multithreading_Concurrency_Thread Safety_Thread Synchronization - Fatal编程技术网

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

Java 通过不同线程同时读取和更改变量,java,multithreading,concurrency,thread-safety,thread-synchronization,Java,Multithreading,Concurrency,Thread Safety,Thread Synchronization,我感兴趣的是一个线程在循环中等待变量更改的情况: while (myFlag == false) { // do smth } 它重复了无数次 同时,另一个线程更改了该变量的值: myFlag = true; 如果这个变量不是易失性的,那么读线程能否看到在另一个线程中更改变量值的结果?总的来说,据我所知,这永远不会发生。还是我错了?那么,在什么时候、什么情况下,第一个线程可以看到变量的变化并退出循环?如果不使用volatile关键字,这是否可行?处理器缓存的大小在这种情况下起作用吗

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

while (myFlag == false) {
    // do smth
}
它重复了无数次

同时,另一个线程更改了该变量的值:

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

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

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

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

总的来说,据我所知,这永远不会发生

不,不是这样的

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

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

我想进一步介绍一下@Jon的优秀答案

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

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

编辑:

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

然而,在我听说的所有体系结构上,执行内存同步的内存屏障的实现都不是特定于字段或对象的。当在
volatile
字段上执行读取并且在特定线程上跨越读取屏障时,它将使用所有中央内存进行更新,而不仅仅是相关的特定
volatile
字段。这与
volatile
写入操作相同。写入
volatile
字段后,本地线程的所有更新都会写入中央内存,而不仅仅是字段。JLS所保证的是,指令不能通过
volatile
访问重新排序


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

这是错误的吗?文章作者写道:“当线程被创建时,它会将所有可访问变量的值复制到自己的堆栈中。如果没有volatile关键字,JVM可以自由地进行一些优化,比如从不刷新某些线程中的本地副本。”因此,在这种情况下,无限循环执行是可能的。还是不是这样?谢谢大家!@是的,无限循环是可能的。但有限循环也是如此。你已经把无限循环的可能性看作是一种保证。仅仅因为虚拟机可以自由地进行某种优化并不意味着它必须这样做。你的帖子并不是在问无限循环是否可能,而是在断言有限循环是不可能的,而事实并非如此。“Java内存模型说,如果任何内存障碍被跨越,所有内存都将被更新。”=>我不这么认为,即使它通常是这样实现的。我认为@assylias是正确的。如果A发生在B之前,B发生在C之前,那么A发生在C之前。但是A必须有一个正式的发生在B发生之前的关系——它在B发生之前按时间顺序发生是不够的。@Gray不是真的——同一个易失性变量的读写之间只有hb。hb确实保证在易失性写入之前的任何写入在易失性读取之后都是可见的,但仅此而已。因此,如果您编写
volatile v1=1
并随后读取
a=volatile v2
,则在
v1=1
之前编写的值不可见是完全合法的。然后您应该编辑您的答案,使其具有“在通用实现中”的效果,而不是“Java内存模型说”,因为后者是一个特定的,例如