すべての同期アクション (揮発性 w/r、ロック/ロック解除など) は、完全な順序を形成します。[1] それは非常に強い声明です。分析が容易になります。volatilev
の場合、この合計順序で、読み取りが書き込みの前であるか、書き込みが読み取りの前です。順序はもちろん実際の実行に依存します。
その全順序から、部分順序の事前発生を確立できます。[2] 変数 (揮発性かどうかに関係なく) に対するすべての読み取りと書き込みが半順序チェーン上にある場合、分析は簡単です。読み取りは直前の書き込みを参照します。これが JMM の主なポイントです。読み取り/書き込みの順序を確立して、順次実行のように推論できるようにします。
しかし、揮発性読み取りが揮発性書き込みの前にある場合はどうなるでしょうか? ここで、もう 1 つの重要な制約が必要です。読み取りは書き込みを参照してはなりません。[3]
したがって、次のように推論できます。
- read of
v
は 0 (初期値) または 2 (揮発性書き込み)のいずれかを認識します
- 2 が表示された場合は、読み取りが書き込みの後にある場合に違いありません。その場合、
happens-before
チェーンがあります。
最後のポイント - の読み取りはi
、への書き込みの 1 つを参照する必要がありますi
。この例では、0 または 1 です。書き込み以外のマジック値は表示されません。
java8仕様を引用:
[1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.4
[2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5
[3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.7
合計順序に関するランダムな考え:
この全体的な順序により、ある同期アクションが別の同期アクションよりも先に発生したと言えます。その時間は壁掛け時計とは一致しないかもしれませんが、私たちの理解にとって悪いメンタル モデルではありません。(実際には、Java での 1 つのアクションはハードウェア アクティビティの嵐に対応し、その時点を定義することは不可能です)
そして、物理的な時間でさえ絶対的なものではありません。光は 1ns で 30cm 進むことに注意してください。今日の CPU では、時間的な順序は間違いなく相対的です。全体的な順序は、実際には、あるアクションから次のアクションへの因果関係があることを必要とします。これは非常に強力な要件であり、JVM がそれを最適化しようと懸命に努力していることは間違いありません。