private volatile static Singleton uniqueInstance
同期にダブル ロック メソッドを使用する場合のシングルトンで、単一インスタンスが volatile として宣言されるのはなぜですか? volatile として宣言せずに同じ機能を実現できますか?
private volatile static Singleton uniqueInstance
同期にダブル ロック メソッドを使用する場合のシングルトンで、単一インスタンスが volatile として宣言されるのはなぜですか? volatile として宣言せずに同じ機能を実現できますか?
これvolatile
により、メモリ書き込みの順序が変更されなくなり、他のスレッドがシングルトンのポインターを介してシングルトンの初期化されていないフィールドを読み取ることができなくなります。
次の状況を考えてみましょう: スレッド A は を発見しuniqueInstance == null
、ロックし、まだ であることを確認し、null
シングルトンのコンストラクターを呼び出します。XYZ
コンストラクターは、 Singleton 内のメンバーに書き込みを行い、戻ります。スレッド A は、新しく作成されたシングルトンへの参照を に書き込み、uniqueInstance
ロックを解放する準備をします。
スレッド A がそのロックを解放する準備ができたのとちょうど同じように、スレッド B がやって来て、それが でuniqueInstance
はないことを発見しnull
ます。スレッドは初期化されたと考えてB
アクセスuniqueInstance.XYZ
しますが、CPU が書き込みの順序を変更したため、スレッド A が書き込んだデータはXYZ
スレッド B から見えるようにはなっていませんXYZ
。
uniqueInstance
volatileとマークすると、メモリ バリアが挿入されます。の書き込みより前に開始されたすべての書き込みはuniqueInstance
、 が変更される前に完了し、uniqueInstance
上記の並べ替えの状況を防ぎます。
コードがないvolatile
と、複数のスレッドで正しく動作しません。
ウィキペディアのダブルチェック ロックから:
J2SE 5.0 では、この問題は修正されています。volatile キーワードにより、複数のスレッドがシングルトン インスタンスを正しく処理できるようになりました。この新しいイディオムは、「ダブルチェック ロックが壊れている」宣言で説明されています。
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
// other functions and members...
}
一般に、ダブルチェック ロックはできれば避けるべきです。ロックを正しく行うのは難しく、間違えるとエラーを見つけるのが難しくなるからです。代わりに、次のより単純なアプローチを試してください。
ヘルパー オブジェクトが静的 (クラス ローダーごとに 1 つ) の場合、代わりに初期化オンデマンド ホルダー イディオムがあります。
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
ダブルロック、または揮発性の使用を避けるために、私は以下を使用します
enum Singleton {
INSTANCE;
}
インスタンスの作成は簡単で、遅延読み込みされ、スレッドセーフです。
揮発性フィールドへの書き込みは、読み取り操作の前に行われます。 以下は、理解を深めるためのコード例です。
private static volatile ResourceService resourceInstance;
//lazy Initialiaztion
public static ResourceService getInstance () {
if (resourceInstance == null) { // first check
synchronized(ResourceService.class) {
if (resourceInstance == null) { // double check
// creating instance of ResourceService for only one time
resourceInstance = new ResourceService ();
}
}
}
return resourceInstance;
}
このリンクは、より良いサービスを提供できます http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html