シングルトンパターンの設計思想は、特定のクラスがアプリケーション内で単一のインスタンスのみを持つことを保証する点にあります。この実現には以下の要素が不可欠です:
- コンストラクタをprivateに設定し、外部からのインスタンス生成を制限
- 静的インスタンスフィールドを用意し、唯一のオブジェクトを保持
- グローバルアクセス可能な静的メソッドを提供してインスタンスを取得
重要なのは、インスタンス生成の制御ではなく「実行時に参照されるオブジェクトの同一性」を保証することです。
主要な実装パターン
初期化方式(スレッドセーフ)
public class InstanceHolder {
private InstanceHolder() {}
private static final InstanceHolder instance = new InstanceHolder();
public static InstanceHolder fetch() {
return instance;
}
}
クラスロード時にインスタンスを生成するため、リソース消費が早いという特徴があります。
遅延初期化(非スレッドセーフ)
public class LazyLoader {
private LazyLoader() {}
private static LazyLoader instance;
public static LazyLoader fetch() {
if (instance == null) {
instance = new LazyLoader();
}
return instance;
}
}
マルチスレッド環境では複数インスタンスが生成される可能性があります。
二重チェックロック(推奨実装)
public class DoubleChecked {
private DoubleChecked() {}
private static volatile DoubleChecked instance;
public static DoubleChecked fetch() {
if (instance != null) return instance;
synchronized (DoubleChecked.class) {
if (instance == null) {
instance = new DoubleChecked();
}
}
return instance;
}
}
volatile修飾子によりJVMの最適化による再orderingを防止し、JDK5以降で有効なスレッドセーフ実装です。
静的ネストクラス方式(最適解)
public class NestedHolder {
private NestedHolder() {}
private static class Holder {
static final NestedHolder instance = new NestedHolder();
}
public static NestedHolder fetch() {
return Holder.instance;
}
}
クラスローダーの仕組みを利用し、インスタンス生成を明示的な参照時まで遅延させます。
列挙型実装(最強の保証)
public enum SafeSingleton {
HOLDER;
public void executeTask() {
// 業務処理
}
}
Java言語仕様によりシリアライズやリフレクション攻撃に対しても完全なシングルトンを保証します。ただしメモリ使用量が静的定数の約2倍になる点に注意が必要です。
汎用コンテナ方式
public class Registry {
private static final Map<String, Object> container = new ConcurrentHashMap<>();
private Registry() {}
public static <T> void register(String key, T obj) {
container.putIfAbsent(key, obj);
}
@SuppressWarnings("unchecked")
public static <T> T resolve(String key) {
return (T) container.get(key);
}
}
複数のシングルトンオブジェクトを一元管理可能ですが、コンストラクタ制限が不完全な点が課題です。