シングルトンとマルチスレッドに関する私の質問を明確にしてください:
- マルチスレッド環境で、Javaでシングルトンを実装するための最良の方法は何ですか?
- 複数のスレッド
getInstance()
が同時にメソッドにアクセスしようとするとどうなりますか? - シングルトンを作成できます
getInstance()
synchronized
か? - シングルトンクラスを使用する場合、同期は本当に必要ですか?
シングルトンとマルチスレッドに関する私の質問を明確にしてください:
getInstance()
が同時にメソッドにアクセスしようとするとどうなりますか?getInstance()
synchronized
か?はい、必要です。遅延初期化でスレッド セーフを実現するために使用できる方法がいくつかあります。
厳格な同期:
private static YourObject instance;
public static synchronized YourObject getInstance() {
if (instance == null) {
instance = new YourObject();
}
return instance;
}
このソリューションでは、実際には最初の数スレッドのみが必要な場合でも、すべてのスレッドを同期する必要があります。
同期を再確認します:
private static final Object lock = new Object();
private static volatile YourObject instance;
public static YourObject getInstance() {
YourObject r = instance;
if (r == null) {
synchronized (lock) { // While we were waiting for the lock, another
r = instance; // thread may have instantiated the object.
if (r == null) {
r = new YourObject();
instance = r;
}
}
}
return r;
}
このソリューションにより、シングルトンを取得しようとする最初のいくつかのスレッドのみがロックを取得するプロセスを実行する必要があることが保証されます。
private static class InstanceHolder {
private static final YourObject instance = new YourObject();
}
public static YourObject getInstance() {
return InstanceHolder.instance;
}
このソリューションは、クラスの初期化に関する Java メモリ モデルの保証を利用して、スレッドの安全性を確保します。各クラスは一度しかロードできず、必要なときにのみロードされます。つまり、最初getInstance
に呼び出され、InstanceHolder
ロードさinstance
れ、作成されます。これは s によって制御されるためClassLoader
、追加の同期は必要ありません。
このパターンは、明示的な同期なしでインスタンスのスレッドセーフな遅延初期化を行います!
public class MySingleton {
private static class Loader {
static final MySingleton INSTANCE = new MySingleton();
}
private MySingleton () {}
public static MySingleton getInstance() {
return Loader.INSTANCE;
}
}
クラスローダーを使用してすべての同期を無料で行うため、これが機能します。クラスはメソッドMySingleton.Loader
内で最初にアクセスされるため、 が初めて呼び出されたときにクラスがロードされます。さらに、クラスローダーは、クラスにアクセスする前にすべての静的初期化が完了することを保証します。これにより、スレッドセーフが実現します。getInstance()
Loader
getInstance()
それは魔法のようです。
これは実際には Jhurtado の列挙型パターンに非常に似ていますが、列挙型パターンは列挙型の概念の乱用であることがわかります (ただし、機能します)。
Java のマルチスレッド環境で作業していて、これらすべてのスレッドがクラスの 1 つのインスタンスにアクセスしていることを保証する必要がある場合は、Enum を使用できます。これには、シリアライゼーションの処理に役立つという追加の利点があります。
public enum Singleton {
SINGLE;
public void myMethod(){
}
}
次に、スレッドに次のようにインスタンスを使用させます。
Singleton.SINGLE.myMethod();
getInstance()
はい、同期する必要があります。そうしないと、クラスの複数のインスタンスを作成できる状況が発生する可能性があります。
getInstance()
同時に呼び出す 2 つのスレッドがある場合を考えてみましょう。次に、T1 がinstance == null
チェックの直後に実行され、次に T2 が実行されるとします。この時点では、インスタンスは作成または設定されていないため、T2 はチェックをパスしてインスタンスを作成します。次に、実行が T1 に戻るとします。これでシングルトンが作成されましたが、T1 はすでにチェックを行っています。オブジェクトを作り直します!同期getInstance()
化すると、この問題を回避できます。
シングルトンをスレッドセーフにする方法はいくつかありますが、getInstance()
同期化するのがおそらく最も簡単です。
静的コード ブロックを使用して、クラスの読み込み時にインスタンスをインスタンス化し、スレッド同期の問題を防ぐこともできます。
public class MySingleton {
private static final MySingleton instance;
static {
instance = new MySingleton();
}
private MySingleton() {
}
public static MySingleton getInstance() {
return instance;
}
}
マルチスレッド環境で Java に Singleton を実装する最良の方法は何ですか?
シングルトンを実装する最良の方法については、この投稿を参照してください。
Javaでシングルトンパターンを実装する効率的な方法は何ですか?
複数のスレッドが同時に getInstance() メソッドにアクセスしようとするとどうなりますか?
メソッドの実装方法によって異なります。volatile 変数を使用せずに二重ロックを使用すると、部分的に構築された Singleton オブジェクトが取得される場合があります。
詳細については、この質問を参照してください。
この二重チェック ロックの例で volatile が使用されている理由
シングルトンの getInstance() を同期させることはできますか?
Singleton クラスを使用する場合、同期は本当に必要ですか?
以下の方法でシングルトンを実装する場合は不要
詳細については、この質問を参照してください
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis () {...}
}
ソース : 有効な Java -> 項目 2
クラスが常にシングルトンのままであることが確実な場合は、それを使用することをお勧めします。