web-dev-qa-db-ja.com

ダブルロックの使用中にシングルトンインスタンスを揮発性にすることの意味は何ですか?

private volatile static Singleton uniqueInstance

シングルトンで同期にダブルロックメソッドを使用する場合、単一のインスタンスが揮発性として宣言されるのはなぜですか? volatileと宣言せずに同じ機能を実現できますか?

35
Phoenix

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;
    }
}
27
Mark Byers

volatileは、メモリへの書き込みの順序が変更されるのを防ぎ、他のスレッドがシングルトンのポインタを通じてシングルトンの初期化されていないフィールドを読み取ることを不可能にします。

この状況を考えてみましょう:スレッドAがuniqueInstance == nullを検出し、ロックし、それがnullであることを確認して、シングルトンのコンストラクターを呼び出します。コンストラクターは、シングルトン内のメンバーXYZに書き込みを行い、戻ります。スレッドAは新しく作成されたシングルトンへの参照をuniqueInstanceに書き込み、そのロックを解放する準備が整います。

スレッドAがロックを解除する準備ができると、スレッドBがやって来て、uniqueInstancenullではないことを発見します。スレッドBは、初期化されていると考えてuniqueInstance.XYZにアクセスしますが、CPUが書き込みの順序を変更したため、スレッドAがXYZに書き込んだデータは、スレッドBには表示されません。 XYZ、これは誤りです。

uniqueInstanceをvolatileとマークすると、メモリバリアが挿入されます。 uniqueInstanceの前に開始されたすべての書き込みは、uniqueInstanceが変更される前に完了します。これにより、上記の並べ替えの状況が回避されます。

55
dasblinkenlight

二重ロックまたは揮発性を使用しないようにするには、以下を使用します

enum Singleton {
     INSTANCE;
}

インスタンスの作成はシンプルで遅延読み込みされ、スレッドセーフです。

11
Peter Lawrey

揮発性フィールドへの書き込みは、読み取り操作の前に行われます。以下は、理解を深めるためのコード例です。

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

0
RajeeV VenkaT