Javaのシングルトンとは何ですか?
シングルトンは、最も単純なクラスレベルの設計パターンの 1 つです。時々、「このクラスはシングルトンです」と言われることがあります。これは、クラスがシングルトン設計パターンを実装していることを意味します。場合によっては、インスタンス化を単一のオブジェクトに制限するクラスを作成する必要があります。たとえば、ロギングや接続を担当するクラスなどです。シングルトン デザイン パターンは、これを実現する方法を説明します。シングルトンは、次の 2 つのことを行うデザイン パターンです。-
これにより、クラスのインスタンスが 1 つだけ存在することが保証されます。
-
これにより、そのインスタンスへのグローバル アクセスの単一ポイントが提供されます。
-
プライベートコンストラクター。これにより、クラス自体の外部でクラスのオブジェクトを作成する機能が制限されます。
-
クラスのインスタンスを返すパブリック静的メソッド。このメソッドはgetInstanceと呼ばれます。これは、クラス インスタンスへのグローバル アクセスのポイントです。
実装オプション
シングルトンデザインパターンはさまざまな方法で適用されます。それぞれのオプションには、それぞれの意味で良い面と悪い面があります。いつものように、ここでも完璧な選択肢はありませんが、私たちは 1 つを目指して努力する必要があります。まず最初に、何が良いか悪いかを判断し、デザイン パターンのさまざまな実装を評価する方法にどのような指標が影響するかを決定しましょう。良いところから始めましょう。実装をより効果的で魅力的なものにする要因は次のとおりです。-
遅延初期化: インスタンスは必要になるまで作成されません。
-
シンプルで透過的なコード: この指標はもちろん主観的なものですが、重要です。
-
スレッド セーフ: マルチスレッド環境での正しい動作。
-
マルチスレッド環境での高いパフォーマンス: リソースの共有時にスレッドがブロックされることがほとんど、またはまったくありません。
-
遅延初期化なし: 必要かどうかに関係なく、アプリケーションの起動時にクラスがロードされるとき (逆説的ですが、IT の世界では遅延するほうが良いのです)
-
複雑で読みにくいコード。この指標も主観的なものです。目が出血し始めた場合は、実装が最適ではないとみなします。
-
スレッドの安全性の欠如。つまり「スレの危険」。マルチスレッド環境での誤った操作。
-
マルチスレッド環境でのパフォーマンスの低下: リソースを共有するときにスレッドが常に、または頻繁に相互にブロックします。
コード
これで、さまざまな実装オプションを検討し、長所と短所を示す準備が整いました。単純
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
最も単純な実装。長所:
-
シンプルで透明なコード
-
スレッドの安全性
-
マルチスレッド環境での高いパフォーマンス
- 遅延初期化はありません。
遅延初期化
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドセーフではありません
同期アクセス
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
長所:
-
遅延初期化。
-
スレッドの安全性
-
マルチスレッドのパフォーマンスが低い
二重チェックロック
public class Singleton {
private static final 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 バージョン以降で修正されています)
クラスホルダー
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;
}
}
長所:
-
遅延初期化。
-
スレッドの安全性。
-
マルチスレッド環境での高いパフォーマンス。
-
正しく動作するには、シングルトンオブジェクトがエラーなく初期化されることが保証される必要があります。それ以外の場合、 getInstanceメソッドへの最初の呼び出しでExceptionInInitializerErrorが発生し、後続のすべての呼び出しで NoClassDefFoundError が生成されます。
実装 | 遅延初期化 | スレッドの安全性 | マルチスレッドのパフォーマンス | いつ使用しますか? |
---|---|---|---|---|
単純 | - | + | 速い | 一度もない。あるいは、遅延初期化が重要ではない場合も考えられます。しかし、これ以上良いことはありません。 |
遅延初期化 | + | - | 適用できない | マルチスレッドが必要ないときは常に |
同期アクセス | + | + | 遅い | 一度もない。あるいは、マルチスレッドのパフォーマンスが重要ではない場合も考えられます。しかし、これ以上良いことはありません。 |
二重チェックロック | + | + | 速い | まれに、シングルトンの作成時に例外を処理する必要がある場合 (クラス ホルダーのシングルトンが適用できない場合) |
クラスホルダー | + | + | 速い | マルチスレッドが必要で、シングルトン オブジェクトが問題なく作成されることが保証されている場合。 |
シングルトン パターンの長所と短所
一般に、シングルトンは期待どおりのことを行います。-
これにより、クラスのインスタンスが 1 つだけ存在することが保証されます。
-
これにより、そのインスタンスへのグローバル アクセスの単一ポイントが提供されます。
-
シングルトンは単一責任の原則に違反します。シングルトン クラスは、その直接の役割に加えて、インスタンスの数も制御します。
-
通常のクラスのシングルトンへの依存は、クラスのパブリック コントラクトには表示されません。
-
グローバル変数はダメです。最終的に、シングルトンは巨大なグローバル変数に変わります。
-
シングルトンが存在すると、アプリケーション全体、特にシングルトンを使用するクラスのテスト容易性が低下します。
GO TO FULL VERSION