シングルトンに関する Wikipedia の記事には、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 番目の例では、1 つのスレッドが最初にインスタンスを取得し、別のスレッドがインスタンスを取得して、最初のスレッドでコンストラクターが終了する前にそのインスタンスに対してアクションを実行しようとした場合はどうなりますか? その場合、安全でない状態になる可能性がありますか?
ベストアンサー1
回答 1:static synchronized
メソッドはクラス オブジェクトをロックとして使用します (つまり、この場合は ) Singleton.class
。
回答 2: Java 言語など:
- クラスが最初にアクセス/使用されたときにロードします
- クラスへのアクセスが許可される前に、すべての静的初期化子が完了していることを保証します。
これら 2 つの事実は、getInstance() メソッドが呼び出されるまで内部の静的クラスSingletonHolder
はロードされないことを意味します。その時点で、呼び出しを行うスレッドにアクセス権が付与される前に、そのクラスの静的インスタンスがクラス ロードの一部としてインスタンス化されます。
これは安全な遅延読み込みを意味します。そしてそれなしどれでも同期/ロックが必要です!
このパターンはのシングルトンに使用するパターン。これは、MyClass.getInstance()
シングルトンの業界標準であるため、他のパターンよりも優れています。このパターンを使用する人は誰でも、シングルトンを扱っていることを自動的に認識します(コードでは、明らかであることは常に良いことです)。そのため、このパターンには適切なAPIがあります。そして内部的には適切な実装です。
ところでビル・ピューの記事シングルトン パターンを理解するには、完全性のために読む価値があります。