シングルトンモードのいくつかの実装
以下に示すように、シングルトンパターンを実装する方法はたくさんあります。
1.怠惰な男、スレッドは安全ではありません
遅延初期化:はい
マルチスレッドセーフですか:いいえ
実現の難しさ:簡単
説明:この方法は最も基本的な実装方法です。この実装の最大の問題は、マルチスレッドをサポートしていないことです。同期されたロックがないため、厳密な意味でのシングルトンモードではありません。
この遅延読み込みの方法は明らかであり、スレッドセーフを必要とせず、複数のスレッドで正しく機能しません。
インスタンス
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
次の実装はすべてマルチスレッドをサポートしていますが、パフォーマンスに違いがあります。
2.怠惰な男、スレッドセーフ
遅延初期化:はい
マルチスレッドセーフですか:はい
実現の難しさ:簡単
説明:このメソッドは遅延読み込みが良好で、複数のスレッドで適切に機能しますが、非常に非効率的であり、99%の場合に同期を必要としません。
利点:最初の呼び出しで初期化し、メモリの浪費を回避します。
短所:シングルトンを確保するには同期をロックする必要がありますが、ロックは効率に影響します。
getInstance()のパフォーマンスは、アプリケーションにとって重要ではありません(このメソッドの使用頻度は低くなります)。
インスタンス
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.空腹の中国人
遅延初期化:いいえ
マルチスレッドセーフですか:はい
実現の難しさ:簡単
説明:このメソッドはより一般的に使用されますが、ガベージオブジェクトを生成するのは簡単です。
利点:ロックしないと、実行効率が向上します。
短所:クラスがロードされたときに初期化し、メモリを浪費します。
マルチスレッド同期の問題を回避するためのクラスローダーメカニズムに基づいています。ただし、インスタンスはクラスの読み込み時にインスタンス化されます。クラスの読み込みには多くの理由がありますが、ほとんどの場合、シングルトンモードでgetInstanceメソッドを呼び出します。クラスの読み込みを引き起こす他の方法(または他の静的メソッド)があります。現時点では、インスタンスを初期化しても、遅延読み込みの効果は明らかに得られません。
インスタンス
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
4.ダブルチェックロック/ダブルチェックロック(DCL、ダブルチェックロック)
JDKバージョン: JDK1.5から
遅延初期化:はい
マルチスレッドセーフですか:はい
実現の難しさ:より複雑
説明:この方法は、安全でマルチスレッドの状況で高いパフォーマンスを維持できるダブルロックメカニズムを使用します。
getInstance()のパフォーマンスは、アプリケーションにとって重要です。
インスタンス
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5.登録済み/静的内部クラス
遅延初期化:はい
マルチスレッドセーフですか:はい
実現の難しさ:平均
説明:この方法は、ダブルチェックロック方法と同じ効果を達成できますが、実現はより簡単です。静的ドメインの遅延初期化は、ダブルチェックロック方式の代わりにこの方法で使用する必要があります。この方法は静的ドメインにのみ適しており、インスタンスドメインを遅延して初期化する必要がある場合は、ダブルチェックロック方法を使用できます。
このメソッドは、クラスローダーメカニズムを使用して、インスタンスの初期化時にスレッドが1つだけであることを確認します。これは、3番目のメソッドとは異なり、シングルトンクラスがロードされている限り、インスタンスはインスタンス化されます(遅延ロードに到達することはありません)。 )、このようにして、シングルトンクラスが読み込まれ、インスタンスは必ずしも初期化されません。SingletonHolderクラスはアクティブに使用されないため、getInstanceメソッドが明示的に呼び出された場合にのみ、SingletonHolderクラスが明示的にロードされてインスタンスがインスタンス化されます。インスタンスのインスタンス化がリソースを消費するため、インスタンスを遅延ロードする場合を想像してみてください。一方、シングルトンクラスもアクティブである可能性があるという保証がないため、ロード時にシングルトンクラスをインスタンス化する必要はありません。他の場所で使用されます。ロードしている場合、現時点でインスタンスをインスタンス化することは明らかに不適切です。現時点では、この方法は3番目の方法よりも合理的です。
インスタンス
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6.列挙
JDKバージョン: JDK1.5から
遅延初期化:いいえ
マルチスレッドセーフですか:はい
実現の難しさ:簡単
説明:この実装は広く採用されていませんが、シングルトンパターンを実装するための最良の方法です。より簡潔で、シリアル化メカニズムを自動的にサポートし、複数のインスタンス化を完全に防ぎます。
このメソッドは、EffectiveJavaの作成者であるJoshBlochによって提唱されています。マルチスレッド同期の問題を回避するだけでなく、シリアル化メカニズムを自動的にサポートして、逆シリアル化による新しいオブジェクトの再作成を防ぎ、複数のインスタンス化を完全に防ぎます。ただし、JDK1.5以降に列挙型機能が追加されたため、このように書くと必然的にさびた感じになり、実際の作業ではほとんど使用されません。
プライベートコンストラクタは、リフレクション攻撃を介して呼び出すことはできません。
インスタンス
public enum Singleton {
INSTANCE;
public void whateverMethod() { }
}
経験の言葉:一般的に、1番目と2番目の怠惰な人の方法を使用することはお勧めできません。3番目の空腹の人の方法を使用することをお勧めします。遅延読み込み効果を明確に実現する場合にのみ、5番目の登録方法が使用されます。オブジェクトを作成するために逆シリアル化が必要な場合は、6番目の列挙方法を試すことができます。他に特別な要件がある場合は、4番目のダブルチェックロック方法の使用を検討できます。