39

事前発生関係が揮発性変数でどのように機能するかを明確にしたいと思います。次の変数があるとします。

public static int i, iDst, vDst;
public static volatile int v;

とスレッド A:

i = 1;
v = 2;

とスレッド B:

vDst = v;
iDst = i;

次のステートメントは、Java メモリ モデル (JMM) に従って正しいですか? そうでない場合、どのような解釈が正しいでしょうか?

  • i = 1常に前発生 v = 2
  • v = 2 JMM での事前 vDst = v発生は、それが時間内に実際に発生した場合にのみ発生します。
  • i = 1 iDst = iJMM での事前発生 (予測iDst可能に割り当てられる1) がv = 2実際に前に発生vDst = vした場合
  • i = 1それ以外の場合、との間の順序iDst = iは未定義であり、 の結果の値もiDst未定義です

ロジックの間違い:

JMM には「ウォール クロック タイム」の概念はなく、 との順序ガイドとして同期順序に依存する必要がv = 2ありvDst = vます。詳細については、選択した回答を参照してください。

4

3 に答える 3

20
  • i = 1常に前発生 v = 2

真実。JLS セクション17.4.5により、

xyが同じスレッドのアクションであり、プログラムの順序でxがyの前にある場合、 hb(x, y) .


  • v = 2 JMM での事前 vDst = v発生は、それが時間内に実際に発生した場合にのみ発生します。
  • i = 1 iDst = iJMM での事前発生 (予測iDst可能に割り当てられる1) がv = 2実際に前に発生vDst = vした場合

間違い。先行発生順序は、物理的な時間内に互いの前に発生することを保証するものではありません。JLSの同じセクションから、

2 つのアクション間に事前発生関係が存在するからといって、必ずしも実装でその順序で実行する必要があるとは限らないことに注意してください。並べ替えが合法的な実行と一致する結果をもたらす場合、それは違法ではありません。

v = 2 ただし、同期順序で前に起こることと前 vDst = vi = 1 起こる iDst = i場合v = 2は保証されます。これは、vDst = vリアルタイムの順序と間違われることが多い、実行の同期アクション全体の順序です。


  • i = 1それ以外の場合、との間の順序iDst = iは未定義であり、 の結果の値もiDst未定義です

vDst = vこれは、同期順序で前に来る場合ですがv = 2、実際の時間は含まれません。

于 2015-05-14T20:43:49.867 に答える
10

はい、先行発生順序に関するこのセクションに従って、それらはすべて正しいです。

  1. i = 1以下から常に先行発生 v = 2:

x と y が同じスレッドのアクションであり、プログラムの順序で x が y の前にある場合、hb(x, y) .

  1. v = 2 揮発性であるvDst = vため、実際に時間内に前に発生した場合にのみ、JMM で発生する前に発生しますv

揮発性フィールド (§8.3.1.4) への書き込みは、そのフィールドの後続のすべての読み取りの前に発生します。

  1. i = 1 実際に時間内に前に発生した場合iDst = i、JMM での事前発生 (予測iDstどおり 1 が割り当てられます) 。これは、この場合、次の理由によるものです。 v = 2vDst = v
    • i = 1 前発生 v = 2
    • v = 2 前発生 vDst = v
    • vDst = v 前発生 iDst = i

hb (x, y)hb(y, z)の場合、hb(x, z) .

編集:

@ user2357112 が主張しているように、ステートメント 2 と 3 は正確に正しくないようです。JLSの同じセクションで説明されているように、先行発生関係は、この関係を持つアクション間に必ずしもタイミングの順序を課すわけではありません。

2 つのアクション間に事前発生関係が存在するからといって、必ずしも実装でその順序で実行する必要があるとは限らないことに注意してください。並べ替えが合法的な実行と一致する結果をもたらす場合、それは違法ではありません。

したがって、JLS で言及されている規則に関しては、ステートメントの実行の実際のタイミングを推測するべきではありません。

于 2015-05-14T20:00:01.617 に答える
6

すべての同期アクション (揮発性 w/r、ロック/ロック解除など) は、完全な順序を形成します。[1] それは非常に強い声明です。分析が容易になります。volatilevの場合、この合計順序で、読み取りが書き込みの前であるか、書き込みが読み取りの前です。順序はもちろん実際の実行に依存します。

その全順序から、部分順序の事前発生を確立できます。[2] 変数 (揮発性かどうかに関係なく) に対するすべての読み取りと書き込みが半順序チェーン上にある場合、分析は簡単です。読み取りは直前の書き込みを参照します。これが JMM の主なポイントです。読み取り/書き込みの順序を確立して、順次実行のように推論できるようにします。

しかし、揮発性読み取りが揮発性書き込みの前にある場合はどうなるでしょうか? ここで、もう 1 つの重要な制約が必要です。読み取りは書き込みを参照してはなりません。[3]

したがって、次のように推論できます。

  1. read ofvは 0 (初期値) または 2 (揮発性書き込み)のいずれかを認識します
  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 がそれを最適化しようと懸命に努力していることは間違いありません。

于 2015-05-14T21:10:41.297 に答える