private volatile static Singleton uniqueInstance
シングルトンで同期にダブルロックメソッドを使用する場合、単一のインスタンスが揮発性として宣言されるのはなぜですか? volatileと宣言せずに同じ機能を実現できますか?
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;
}
}
volatile
は、メモリへの書き込みの順序が変更されるのを防ぎ、他のスレッドがシングルトンのポインタを通じてシングルトンの初期化されていないフィールドを読み取ることを不可能にします。
この状況を考えてみましょう:スレッドAがuniqueInstance == null
を検出し、ロックし、それがnull
であることを確認して、シングルトンのコンストラクターを呼び出します。コンストラクターは、シングルトン内のメンバーXYZ
に書き込みを行い、戻ります。スレッドAは新しく作成されたシングルトンへの参照をuniqueInstance
に書き込み、そのロックを解放する準備が整います。
スレッドAがロックを解除する準備ができると、スレッドBがやって来て、uniqueInstance
がnull
ではないことを発見します。スレッドB
は、初期化されていると考えてuniqueInstance.XYZ
にアクセスしますが、CPUが書き込みの順序を変更したため、スレッドAがXYZ
に書き込んだデータは、スレッドBには表示されません。 XYZ
、これは誤りです。
uniqueInstance
をvolatileとマークすると、メモリバリアが挿入されます。 uniqueInstance
の前に開始されたすべての書き込みは、uniqueInstance
が変更される前に完了します。これにより、上記の並べ替えの状況が回避されます。
二重ロックまたは揮発性を使用しないようにするには、以下を使用します
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