シングルトンとは何ですか?
シングルトンは、クラスに適用できる最も単純な設計パターンの 1 つです。「このクラスはシングルトンです」と言われることがありますが、これは、このクラスがシングルトン設計パターンを実装していることを意味します。場合によっては、オブジェクトを 1 つだけ作成できるクラスを作成することが必要になることがあります。たとえば、データベースへのログ記録や接続を担当するクラスです。シングルトン設計パターンは、そのようなタスクをどのように達成できるかを説明します。シングルトンは、次の 2 つのことを行うデザイン パターンです。-
クラスがそのクラスのインスタンスを 1 つだけ持つことを保証します。
-
このクラスのインスタンスにグローバル アクセス ポイントを提供します。
-
プライベートコンストラクター。クラス自体の外部にクラス オブジェクトを作成する機能を制限します。
-
クラスのインスタンスを返すパブリック静的メソッド。このメソッドは と呼ばれます
getInstance
。これは、クラス インスタンスへのグローバル アクセス ポイントです。
実装オプション
シングルトン デザイン パターンはさまざまな方法で使用されます。それぞれのオプションには、それぞれの意味で良い面と悪い面があります。いつものように、理想はありませんが、理想に向かって努力する必要があります。しかし、まず最初に、何が良くて何が悪いのか、またデザイン パターンの実装の評価にどのような指標が影響するのかを定義しましょう。ポジティブなことから始めましょう。実装に魅力と魅力を与える基準は次のとおりです。-
遅延初期化: アプリケーションの実行中に必要なときにクラスがロードされるとき。
-
コードの単純さと透明性: 指標はもちろん主観的なものですが、重要です。
-
スレッド セーフ: マルチスレッド環境で正しく動作します。
-
マルチスレッド環境での高いパフォーマンス: リソースを共有する際、スレッドは相互に最小限にブロックするか、まったくブロックしません。
-
非遅延初期化: 必要かどうかに関係なく、アプリケーションの起動時にクラスがロードされるとき (矛盾していますが、IT の世界では遅延しているほうが良いのです)
-
コードが複雑で可読性が低い。指標も主観的なものです。目から血が出ている場合、実装はまあまあであると仮定します。
-
スレッドの安全性の欠如。つまり「スレッドハザード」です。マルチスレッド環境での誤った操作。
-
マルチスレッド環境でのパフォーマンスの低下: リソースを共有するときにスレッドが常に、または頻繁に相互にブロックします。
コード
これで、長所と短所を列挙して、さまざまな実装オプションを検討する準備が整いました。シンプルな解決策
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
最も単純な実装。長所:
-
コードのシンプルさと透明性
-
スレッドの安全性
-
マルチスレッド環境での高いパフォーマンス
- 遅延初期化ではありません。
遅延初期化
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドセーフではありません
同期アクセサー
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドの安全性
-
マルチスレッド環境でのパフォーマンスの低下
getInstance
同期され、一度に 1 つずつしか入力できなくなります。実際、メソッド全体を同期する必要はなく、新しいクラス オブジェクトを初期化する部分だけを同期する必要があります。synchronized
しかし、新しいオブジェクトの作成を担当する部分を単純にブロックでラップすることはできません。これでは、スレッドの安全性が確保されません。もう少し複雑です。正しい同期方法は次のとおりです。
二重チェックされたロック
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドの安全性
-
マルチスレッド環境での高いパフォーマンス
-
1.5 より前の Java バージョンではサポートされていません (volatile キーワードはバージョン 1.5 で修正されました)
INSTANCE
かである必要があります。今日説明する最後の実装は です。 final
volatile
Class Holder Singleton
クラスホルダーシングルトン
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドの安全性。
-
マルチスレッド環境での高いパフォーマンス。
-
正しく動作するには、クラス オブジェクトが
Singleton
エラーなく初期化されることを保証する必要があります。そうしないと、最初のメソッド呼び出しはgetInstance
エラーで終了しExceptionInInitializerError
、後続のメソッド呼び出しはすべて失敗しますNoClassDefFoundError
。
実装 | 遅延初期化 | スレッドの安全性 | マルチスレッド速度 | いつ使用しますか? |
---|---|---|---|---|
シンプルな解決策 | - | + | 速い | 一度もない。または、遅延初期化が重要ではない場合。しかし、決して良くありません。 |
遅延初期化 | + | - | 適用できない | マルチスレッドが必要ないときは常に |
同期アクセサー | + | + | ゆっくり | 一度もない。または、マルチスレッドによる作業の速度が重要ではない場合。しかし、決して良くなるわけではありません |
二重チェックされたロック | + | + | 速い | まれに、シングルトンの作成時に例外を処理する必要がある場合があります。(クラスホルダーシングルトン非適用時) |
クラスホルダーシングルトン | + | + | 速い | マルチスレッドが必要な場合は常に、シングルトン クラス オブジェクトが問題なく作成されることが保証されます。 |
シングルトン パターンの長所と短所
一般に、シングルトンは期待どおりの動作をします。-
クラスがそのクラスのインスタンスを 1 つだけ持つことを保証します。
-
このクラスのインスタンスにグローバル アクセス ポイントを提供します。
-
Singleton は SRP (単一責任原則) に違反しています。Singleton クラスは、その直接の責任に加えて、そのコピーの数も制御します。
-
シングルトンに対する通常のクラスまたはメソッドの依存関係は、クラスのパブリック コントラクトには表示されません。
-
グローバル変数はダメです。シングルトンは最終的に 1 つの大きなグローバル変数に変わります。
-
シングルトンが存在すると、アプリケーション全般、特にシングルトンを使用するクラスのテスト容易性が低下します。
GO TO FULL VERSION