同期前にメモリを更新しますか?
-
13-12-2019 - |
質問
Javaメモリモデルで述べられていることが言及されています。スレッドが関連するモニタを解放する部分として同期ブロックを終了すると、JMMはローカルプロセッサキャッシュをメインメモリにフラッシュする必要があります。同様に、モニタを取得する部分として同期ブロックを入力するときに、ローカルキャッシュは無効になり、後続の読み取りはローカルキャッシュではなくメインメモリに直接送信されます。
では、そのコードでは、2番目のスレッドが同期ブロックが入力されるとメインメモリに直接アクセスするため、そのコードでは不安定にインスタンスを宣言しなければならないのはなぜですか??
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
.
私は別のスレッドが同期ブロックに入っていて2番目のチェックを行うと、上述のようにメインメモリから更新することになっていることを意味します。
解決
競走状態はこれです:
-
スレッドAは
instance == NULL
を見て、このコードinstance = new MySingleton();
を実行しています。instance
への書き込みは表示されますが、MySingleton
への書き込みはまだありません。 -
スレッドBは
instance != NULL
を見て、インスタンスの作業を開始します。 -
スレッドBは、それが見ることができない構造のオブジェクトに取り組んでいます。
instance
の揮発性がJDK5のようにJDKメモリ仕様として問題を解決し、不揮発性オブジェクトに書き込むことは不安定なオブジェクトへの書き込みに対して順序から見られないことを保証します。そのため、instance != NULL
を見ているスレッドは、インスタンス自体を見なければなりません。
他のヒント
volatileを宣言する必要があります。getInstance()の2つの呼び出しが同じインスタンスを返すという保証はありません。
メインメモリにアクセスされるという保証はありません。すなわち、すべてのスレッドが同じ値を見ることができます。
BTW:あなたはもちろん必要以上に複雑なものを知っています。あなたが必要とするのは
ですpublic enum MySingleton {
INSTANCE;
}
.
はほとんど同じことをしていません。