web-dev-qa-db-ja.com

`synchronized(new Object()){}`が何もしないのはなぜですか?

次のコードでは:

class A {
    private int number;

    public void a() {
        number = 5;
    }

    public void b() {
        while(number == 0) {
            // ...
        }
    }
}

メソッドbが呼び出され、メソッドaを起動する新しいスレッドが開始された場合、メソッドbはnumberの変更を確認できないため、bが終了することはありません。

もちろん、これを解決するためにnumbervolatileを作成できます。ただし、学術的な理由から、volatileはオプションではないと仮定しましょう。

JSR-133 FAQs は私たちに伝えます:

同期されたブロックを終了した後、モニターを解放します。これはキャッシュをメインメモリにフラッシュする効果がありますなので、このスレッドによる書き込みは他のスレッドから見えるようになります。同期されたブロックに入る前に、モニターを取得しますローカルプロセッサキャッシュを無効にする効果がありますなので、変数がメインメモリから再ロードされます。

これは、a- Blockに出入りするには、どのモニターを使用していても、bsynchronizedの両方が必要なようです。より正確には、このように聞こえます...:

class A {
    private int number;

    public void a() {
        number = 5;
        synchronized(new Object()) {}
    }

    public void b() {
        while(number == 0) {
            // ...
            synchronized(new Object()) {}
        }
    }
}

...問題を解消し、baへの変更を認識し、最終的に終了することを保証します。

ただし、FAQには次のようにも明記されています。

もう1つの影響は、一部の人々がメモリバリアを強制するために使用する次のパターンが機能しないことです。

synchronized (new Object()) {}

これは実際には何もせず、コンパイラーはそれを完全に削除できます。コンパイラーは、同じモニター上で他のスレッドが同期しないことを知っているからです。別のスレッドの結果を表示するには、1つのスレッドに「前に発生」関係を設定する必要があります。

今では混乱しています。同期ステートメントはキャッシュをフラッシュさせると思いました。それは確かにメインメモリへのキャッシュをフラッシュすることができないので、メインメモリの変更は同じモニターで同期するスレッドによってのみ見ることができます。特に、基本的に同じことを行うvolatileの場合は、監視しますか、それとも私は間違っていますか?では、なぜこれが何もせず、bが保証によって終了しないようにするのですか?

53
yankee

FAQは問題の権限ではありません。JLSはそうです。セクション 17.4.4 は、先に発生する関係(17.4。 5)。関連する箇条書きは次のとおりです。

  • モニターのロック解除アクションmsynchronizes-with以降のすべてのロックアクションm(「後続」は同期順序に従って定義されます)。

mnew Object()への参照であり、他のスレッドに保存または公開されることはないため、次のことが確実になります。このブロックのロックが解放された後、他のスレッドはmのロックを取得しません。さらに、mは新しいオブジェクトであるため、以前にロック解除されたアクションがないことを確認できます。したがって、このアクションと正式に同期するアクションはありません。

技術的には、JLS仕様に準拠するためにキャッシュを完全にフラッシュする必要はありません。 JLSが必要とする以上のものです。 typicalの実装は、ハードウェアで実行できる最も簡単な方法ですが、いわば「それ以上」に進んでいるためです。 エスケープ分析 を使用すると、最適化コンパイラーに必要な数がさらに少なくなると、コンパイラーはより少ないパフォーマンスで実行できます。あなたの例では、エスケープ分析は、(上記の理由により)アクションが効果がないことをコンパイラに通知し、完全に最適化することができます。

49
yshavit

一部の人々がメモリバリアを強制するために使用する次のパターンは機能しません:

無操作であることが保証されていませんが、仕様では無操作であることが許可されています。仕様では、2つのスレッドが同じオブジェクトで同期するときに、2つのスレッド間で発生前の関係を確立するために同期が必要ですが、実際には、オブジェクトのIDが重要ではないJVMを実装する方が簡単です。

同期ステートメントはキャッシュをフラッシュさせると思いました

Java言語仕様)には「キャッシュ」はありません。これは、一部の(つまり、事実上すべての)ハードウェアプラットフォームとJVM実装の詳細にのみ存在する概念です。

20
Solomon Slow