41
private volatile static Singleton uniqueInstance

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

4

5 に答える 5

66

これvolatileにより、メモリ書き込みの順序が変更されなくなり、他のスレッドがシングルトンのポインターを介してシングルトンの初期化されていないフィールドを読み取ることができなくなります。

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

スレッド A がそのロックを解放する準備ができたのとちょうど同じように、スレッド B がやって来て、それが でuniqueInstanceはないことを発見しnullます。スレッドは初期化されたと考えてBアクセスuniqueInstance.XYZしますが、CPU が書き込みの順序を変更したため、スレッド A が書き込んだデータはXYZスレッド B から見えるようにはなっていませんXYZ

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

于 2012-07-24T22:05:26.777 に答える
32

コードがない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;
    }
}
于 2012-07-24T21:44:07.633 に答える
10

ダブルロック、または揮発性の使用を避けるために、私は以下を使用します

enum Singleton {
     INSTANCE;
}

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

于 2012-07-25T07:16:14.823 に答える
1

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

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

于 2016-06-08T07:26:37.383 に答える