web-dev-qa-db-ja.com

Javaのスレッドセーフシングルトン

シングルトンに関するウィキペディアの記事では、Javaで構造を実装するいくつかのスレッドセーフな方法について言及しています。私の質問では、長い初期化手順があり、一度に多くのスレッドがアクセスするシングルトンについて考えてみましょう。

まず、この言及されていないメソッドはスレッドセーフであり、その場合、何に同期しますか?

_public class Singleton {
    private Singleton instance;

    private Singleton() {
        //lots of initialization code
    }

    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
_

次に、次の実装はなぜスレッドセーフで初期化が面倒なのですか? 2つのスレッドが同時にgetInstance()メソッドに入った場合、正確にはどうなりますか?

_public class Singleton {
    private Singleton() {
        //lots of initialization code
    }

    private static class SingletonHolder { 
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
_

最後に、2番目の例では、あるスレッドが最初にインスタンスを取得し、別のスレッドがインスタンスを取得して、コンストラクターが最初のスレッドで完了する前にインスタンスでアクションを実行しようとするとどうなるでしょうか。では、危険な状態に入ることができますか?

38
donnyton

回答1:_static synchronized_メソッドは、クラスオブジェクトをロックとして使用します。つまり、この場合は_Singleton.class_です。

回答2:Java言語、とりわけ:

  • 最初にアクセス/使用されたときにクラスをロードします
  • クラスへのアクセスが許可される前に、すべての静的初期化子が完了していることを保証します

これらの2つの事実は、getInstance()メソッドが呼び出されるまで、内部静的クラスSingletonHolderがロードされないことを意味します。その時点で、呼び出しを行うスレッドにアクセスが許可される前に、そのクラスの静的インスタンスがクラスのロードの一部としてインスタンス化されます。

つまり、安全な遅延読み込みおよびがあり、同期/ロックにanyする必要はありません!

このパターンは、シングルトンに使用するtheパターンです。 MyClass.getInstance()はシングルトンの事実上の業界標準であるため、他のパターンに勝っています。これを使用するすべての人は、シングルトンを処理していることを自動的に認識します(コードを使用すると、常に明白であることが望ましいです)。適切なAPIおよび内部での適切な実装。

btw Bill Pughの記事 は、シングルトンパターンを理解する上で、完全に読む価値があります。

46
Bohemian