Java 无内存障碍的无序写入:数据竞争的唯一可能原因?
在Brian Goetz的Java并发实践中,我遇到了以下几行: 当一个变量被多个线程读取时,就会发生数据争用, 和至少由一个线程写入,但读取和写入不是 排序依据发生在之前。一个正确同步的程序就是其中之一 没有数据竞争;正确同步的程序显示顺序 一致性,意味着程序中的所有操作看起来都是一致的 以固定的全局顺序发生 我的问题是,在java或其他编程语言中,无序写入是造成数据竞争状况的唯一原因吗?Java 无内存障碍的无序写入:数据竞争的唯一可能原因?,java,multithreading,concurrency,happens-before,Java,Multithreading,Concurrency,Happens Before,在Brian Goetz的Java并发实践中,我遇到了以下几行: 当一个变量被多个线程读取时,就会发生数据争用, 和至少由一个线程写入,但读取和写入不是 排序依据发生在之前。一个正确同步的程序就是其中之一 没有数据竞争;正确同步的程序显示顺序 一致性,意味着程序中的所有操作看起来都是一致的 以固定的全局顺序发生 我的问题是,在java或其他编程语言中,无序写入是造成数据竞争状况的唯一原因吗? 更新 好的,我对数据竞赛做了更多的调查,发现如下所示: 线程分析器检测执行期间发生的数据争用 一个多线程
更新
好的,我对数据竞赛做了更多的调查,发现如下所示: 线程分析器检测执行期间发生的数据争用 一个多线程进程的。数据竞争发生在以下情况:
- 单个进程中的两个或多个线程同时访问同一内存位置,以及
- 至少有一个访问用于写入,以及
- 线程没有使用任何独占锁来控制对该内存的访问
在本部分中,需要提到:访问顺序是不确定的
它是在谈论线程访问内存位置的顺序吗?如果是,那么同步永远不能保证线程访问代码块的顺序。那么,同步如何解决数据竞争的问题呢?
我宁愿将数据竞争定义为 当读取结果由“内部”(jvm或操作系统控制的)线程调度决定时,从变量写入和读取某个值或引用之间的数据竞争是一种情况 事实上,问题的第二个定义在更“官方”的词语中也是如此:)
换句话说,考虑线程A给变量和线程B写一些值,试图读取它。如果您错过了任何类型的同步(或在写入和后续读取之间的保证之前发生的能够提供的其他机制),那么您的程序在线程a和线程B之间存在数据竞争
现在,回答你的问题: 它是在谈论线程访问内存位置的顺序吗?如果是,那么同步永远不能保证线程访问代码块的顺序 在这种特殊情况下,同步保证了在writer线程退出synchronized
block或method后写入新值之前,您永远无法读取变量的值。如果没有同步,即使在实际发生写操作之后,也有可能读取旧值
关于访问顺序:它将通过以下方式确定同步:
让我们再看看线程a和B。操作顺序现在是连续的-在线程A完成写入之前,线程B将无法开始读取。为了弄清楚这种情况,想象一下写作和阅读确实是一个漫长的过程。如果没有同步,这些操作将能够相互交错,这可能导致读取一些无意义的值。可以。这里的问题是内存可见性。除非您已将变量标记为volatile,或者正在使用synchronized block或lock保护其访问,否则给定线程可能看不到另一个线程所做的更新。@Mac:以原子方式写入该变量是关键。Java支持的原子操作(例如,
Java.util.concurrent.atomic.*
)保证操作是有序的。因此,如果一个线程更新一个原子变量,那么更新将在所有后续读取之前进行。原子操作本身设置了内存屏障。如果您不能以原子方式(或使用同步或锁定)更新变量,那么@BrettOkken的注释中提到的情况可能会发生happen@Mac:这都是关于记忆障碍的。原子操作、可见性、同步和锁都是关于设置内存屏障,以便对特定数据块的操作具有适当的排序语义。我们需要小心谈论原子操作。设置任何基元(double或long除外)都是原子的。这并不意味着它跨线程可见。@jameslarge这两者并不一定冲突。有些数据竞争可能确实是良性的,但程序仍然会被归类为未正确同步。