Java synchronized キーワードはキャッシュをフラッシュしますか?
-
25-09-2019 - |
質問
Java 5 以降のみ。マルチプロセッサの共有メモリ コンピュータを想定します (おそらく現在もそれを使用しているでしょう)。
シングルトンの遅延初期化のコードは次のとおりです。
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;
}
}
する instance
宣言しなければならない volatile
オプティマイザが getInstance() を次のように書き換えるのを防ぐためです (逐次プログラムではこれが正しいでしょう)。
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
// instance must be null or we wouldn't be here (WRONG!)
instance = new MySingleton();
}
}
}
オプティマイザがコードを書き換えないと仮定すると、 instance
宣言されていない volatile
のときにメモリにフラッシュされることが保証されていますか? synchronized
ブロックが終了し、次の時点でメモリから読み取られます。 synchronized
ブロックが入りましたか?
編集:getInstance() を静的にするのを忘れていました。それによって答えの妥当性が変わるとは思いません。皆さんは私が何を言いたいのか分かっていました。
解決
はい、 instance
宣言されるべきである volatile
. 。その場合でも、二重チェック ロックを使用しないことをお勧めします。これ (正確には Java メモリ モデル) には、部分的に実装されたオブジェクトの公開を許可する重大な欠陥がありました。これは Java5 で修正されましたが、依然として DCL は時代遅れのイディオムであり、もう使用する必要はありません。 遅延初期化ホルダーのイディオム その代わり。
から 実際の Java 同時実行性, 、セクション 16.2:
DCL の本当の問題は、同期せずに共有オブジェクト参照を読み取るときに起こり得る最悪の事態は、誤って古い値を認識することである (この場合、
null
);その場合、DCL イディオムは、ロックを保持したまま再試行することでこのリスクを補います。しかし、最悪のケースは実際にはかなり悪くなります。参照の現在値は表示されるものの、オブジェクトの状態の値が古い場合があります。つまり、オブジェクトが無効または不正な状態にあると見なされる可能性があります。その後の JMM (Java 5.0 以降) の変更により、DCL が機能するようになりました。 もし
resource
作られていますvolatile
, 、これによるパフォーマンスへの影響は小さいです。volatile
読み取りは通常、不揮発性読み取りよりもわずかにコストがかかるだけです。ただし、これは実用性がほとんどなくなった慣用句です。これを動機づけた力 (競合のない同期が遅い、JVM の起動が遅い) が機能しなくなったため、最適化としての効果が低くなります。遅延初期化ホルダー イディオムには同じ利点があり、理解しやすくなっています。
他のヒント
はい、インスタンスはJavaでのダブルチェックロックを使用して揮発性である必要があります。MySingletonの初期化により、部分的に構成されたオブジェクトがシステムの残りの部分に公開される可能性があるためです。また、スレッドが「同期した」ステートメントに達すると同期することも事実ですが、この場合は遅すぎます。
ウィキペディア また、他のいくつかのスタックオーバーフローの質問は、「ダブルチェックロック」について良い議論をしているので、それについて読むことをお勧めします。また、プロファイリングがこの特定のコードでパフォーマンスが必要であることを示さない限り、それを使用しないことをお勧めします。
Javaシングルトンの怠zyな初期化のためのより安全で読みやすいイディオムがあります:
class Singleton {
private static class Holder {
static final Singleton instance = create();
}
static Singleton getInstance() {
return Holder.instance;
}
private Singleton create() {
⋮
}
private Singleton() { }
}
より冗長なダブルチェックロックパターンを使用する場合、フィールドを宣言する必要があります volatile
, 、他の人がすでに指摘しているように。
いいえ。揮発性である必要はありません。見る Javaの「ダブルチェックロックが壊れている」宣言を解決する方法は?
Javaをチートして揮発性/同期を避けることができれば、Javaがあなたをだましてオブジェクトの誤ったビューを与えることができるため、以前の試みはすべて失敗しました。しかし、新しい final
セマンティクスは問題を解決します。オブジェクト参照を取得した場合(通常の読み取りで)、安全に読むことができます final
田畑。