私は最近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 がどのように説明しているか説明できますか?
その他のコメント:
いくつかの回答に基づいて、皆さんが言っていることにさらにコメントを書きたいと思います。
- シングルスレッド内でのリオーダー
私が引用したソースからのいくつかの「真実」:
a = new A()
new A()
100 の操作が含まれ、その後にヒープ上のアドレスが に割り当てられる場合a
、コンパイラはそれらを単純に並べ替えてヒープ アドレスを割り当て、a
通常の初期化に従うことができます (ダブル チェック ロックの問題)。
synchronized{
a = 5;
}
print a;
に変更できます
synchronized{
a = 5;
print a;
}
SOはmonitorexit
printステートメントで再注文しました(JLSに従っても有効です)
さて、私が言及した単純なケース:
x = 1;
y = 2;
c = x + y;
print c;
コンパイラが最初に x または y を割り当てるのを止める理由はわかりません。x が最初に割り当てられるか y が割り当てられるかに関係なく、最終的な出力は変更されないため、それを止めるものはまったくありません。したがって、並べ替えは完全に可能です。
- モニター.ロック解除
print ステートメントが同期ブロックに「引き込まれている」例に基づいて、これを逆にしてみましょう。
synchronized{
a = 5;
print a;
}
コンパイラがこれを行うことを期待できます:
synchronized{
a = 5;
}
print a;
シングルスレッドの世界では完全に合理的に見えますが、これは間違いなく無効であり、 JLSに反します (引用元によると)。これについてJLS内で何も見つからない場合、なぜそうなるのでしょうか? そして明らかに、「プログラムの順序」に関する動機は今や無関係です。コンパイラーは、ステートメントを同期ブロックに「引き込む」などのリオーダーを行うことができるからです。