3

私は最近http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmlを読みました.Javaメモリモデルの多くの組み込みを明確に説明しています. ある特定の抜粋が私の注意を引きました。

The rule for a monitorexit (i.e., releasing synchronization) is that 
actions before the monitorexit must be performed before the monitor is released.

私には明らかなようですが、http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htmlと事前発生の定義を読んだ後、モニターのブロック解除について見つけることができたのは、スレッドは、他のスレッドが再びロックする前に発生するモニターのロックを解除します(これも完全に理にかなっています)。同期ブロック内のすべてのアクションがロック解除操作の前に発生する必要があるという明白な条件を JLS がどのように説明しているか説明できますか?

その他のコメント:

いくつかの回答に基づいて、皆さんが言っていることにさらにコメントを書きたいと思います。

  1. シングルスレッド内でのリオーダー

私が引用したソースからのいくつかの「真実」:

a = new A()

new A()100 の操作が含まれ、その後にヒープ上のアドレスが に割り当てられる場合a、コンパイラはそれらを単純に並べ替えてヒープ アドレスを割り当て、a通常の初期化に従うことができます (ダブル チェック ロックの問題)。

synchronized{
    a = 5;
}
print a;

に変更できます

synchronized{
    a = 5;
    print a;
}

SOはmonitorexitprintステートメントで再注文しました(JLSに従っても有効です)

さて、私が言及した単純なケース:

x = 1;
y = 2;
c = x + y;
print c;

コンパイラが最初に x または y を割り当てるのを止める理由はわかりません。x が最初に割り当てられるか y が割り当てられるかに関係なく、最終的な出力は変更されないため、それを止めるものはまったくありません。したがって、並べ替えは完全に可能です。

  1. モニター.ロック解除

print ステートメントが同期ブロックに「引き込まれている」例に基づいて、これを逆にしてみましょう。

synchronized{
    a = 5;
    print a;
}

コンパイラがこれを行うことを期待できます:

synchronized{
    a = 5;
}
 print a;

シングルスレッドの世界では完全に合理的に見えますが、これは間違いなく無効であり、 JLSに反します (引用元によると)。これについてJLS内で何も見つからない場合、なぜそうなるのでしょうか? そして明らかに、「プログラムの順序」に関する動機は今や無関係です。コンパイラーは、ステートメントを同期ブロックに「引き込む」などのリオーダーを行うことができるからです。

4

2 に答える 2

2

synchronizedブロック内で実行されるすべてのアクションだけでなく、 monitorexit.

同期ブロック内のすべてのアクションがロック解除操作の前に発生する必要があるという明白な条件を JLS がどのように説明しているか説明できますか?

特定のスレッド (および 1 つのスレッドのみ)synchronizedでは、プログラムの順序に関係なくすべてのアクションが維持されるため、すべての読み取りと書き込みが順番に行われるように見えます (単一スレッドの場合、事前発生順序付けは必要ありません)。

先行発生の関係では、複数のスレッドが考慮されます。つまり、monitorexit の前に 1 つのスレッドで発生したすべてのアクションは、後続のmonitorenter.

編集して更新に対処します。

並べ替えを行うには、コンパイラが従わなければならない特定の規則があります。この場合の特定のものは、ここにある Can Reorder グリッドに示されます

特に有用なエントリは次のとおりです。

  • 最初のアクション: 通常の読み込み (load a; print a)
  • 2 番目のアクション: モニターの終了

ここでの値はNoです。つまり、コンパイラは、最初が通常のロードで、2 番目が 2 つのアクションを並べ替えることができないmonitorexitため、この並べ替えは JLS に違反します。

roach-motel の順序付けとして知られる規則があります。つまり、読み取り/書き込みを同期ブロックに並べ替えることができますが、同期ブロックからは並べ替えることができません。

于 2014-02-18T13:09:25.710 に答える
1

これを見逃したかもしれません(§17.4.5):

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

事前に発生することについて既に知っていることと組み合わせると、ロック解除アクションに先行するすべてのアクションが他のスレッドに表示されることを意味することは明らかです。

質問への追加について、これを書くと:

synchronized {
    a = 5;
    b = 3;
}

コンパイラはこれを発行します:

synchronized{
    a = 5;
}
b = 3;

次に、上で引用した規定に違反します。今 はlock-releaseb = 3の前に発生しません。これが違法である理由です。(あなたの例は、単純な変数では簡単に説明できない読み取り+副作用のみを含むため、有益ではないことに注意してください。)print a

于 2014-02-18T13:14:26.127 に答える