回答:
はい、必要です。遅延初期化でスレッドセーフを実現するために使用できる方法がいくつかあります。
ドラコニアン同期:
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
れ、作成されます。これはClassLoader
s によって制御されるため、追加の同期は必要ありません。
Draconian synchronization
およびDouble check synchronization
getInstance()メソッドは静的でなければなりません!
static
である場合はより理にかなっている可能性があります。要求に応じて修正。
r
正確さのために必要ではありません。ローカル変数にアクセスするよりもはるかにコストがかかるため、揮発性フィールドへのアクセスを回避するための最適化にすぎません。
このパターンは、明示的な同期なしで、インスタンスのスレッドセーフな遅延初期化を実行します。
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()
。さらに、クラスローダーは、クラスにアクセスする前にすべての静的初期化が完了することを保証します。これがスレッドの安全性をもたらします。
まるで魔法のようです。
それは実際にはジュルタドの列挙型パターンに非常に似ていますが、列挙型パターンは列挙型概念の乱用であることがわかります(ただし、機能します)
final
追加する必要があります。できました。
はい、作る必要があります getInstance()
同期。そうでない場合、クラスの複数のインスタンスを作成できる状況が発生する可能性があります。
getInstance()
同時に呼び出す2つのスレッドがある場合を考えます。ここで、T1がinstance == null
チェックの直後を実行し、次にT2が実行されることを想像してください。この時点ではインスタンスは作成または設定されていないため、T2はチェックに合格してインスタンスを作成します。ここで、実行がT1に切り替わったとします。これでシングルトンが作成されましたが、T1はすでにチェックを完了しています。オブジェクトの作成を再開します!getInstance()
同期させると、この問題を回避できます。
シングルトンをスレッドセーフにする方法はいくつかありますが、getInstance()
同期させるのがおそらく最も簡単です。
列挙型シングルトン
スレッドセーフなシングルトンを実装する最も簡単な方法は、列挙型を使用することです
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
このコードはJava 1.5のEnumの導入以来機能します
ダブルチェックロック
マルチスレッド環境(Java 1.5以降)で機能する「クラシック」シングルトンをコーディングする場合は、これを使用する必要があります。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
1.5以前は、volatileキーワードの実装が異なるため、これはスレッドセーフではありません。
シングルトンの早期ロード(Java 1.5より前でも機能)
この実装は、クラスが読み込まれたときにシングルトンをインスタンス化し、スレッドセーフを提供します。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void doSomething(){
System.out.println("This is a singleton");
}
}
静的コードブロックを使用して、クラスの読み込み時にインスタンスをインスタンス化し、スレッド同期の問題を回避することもできます。
public class MySingleton {
private static final MySingleton instance;
static {
instance = new MySingleton();
}
private MySingleton() {
}
public static MySingleton getInstance() {
return instance;
}
}
instance
ファイナルにする2. getInstance()
スタティックにする。
マルチスレッド環境でJavaにシングルトンを実装する最良の方法は何ですか?
シングルトンを実装する最良の方法については、この投稿を参照してください。
Javaでシングルトンパターンを実装する効率的な方法は何ですか?
複数のスレッドが同時にgetInstance()メソッドにアクセスしようとするとどうなりますか?
これは、メソッドの実装方法によって異なります。揮発性変数なしで二重ロックを使用すると、部分的に構築されたシングルトンオブジェクトが取得される場合があります。
詳細については、この質問を参照してください。
このダブルチェックロックの例でvolatileが使用される理由
シングルトンのgetInstance()を同期化できますか?
シングルトンクラスを使用する場合、同期は本当に必要ですか?
以下の方法でシングルトンを実装する場合は不要
詳細については、この質問を参照してください