シングルトンパターン


シングルトン パターンは、Java で最も単純なデザイン パターンの 1 つです。このタイプのデザイン パターンは創造的なパターンであり、オブジェクトを作成するための最適な方法を提供します。

このパターンには、単一のオブジェクトのみが作成されるようにしながら、独自のオブジェクトの作成を担当する単一のクラスが含まれます。このクラスは、クラスのオブジェクトをインスタンス化することなく、その固有のオブジェクトに直接アクセスする方法を提供します。

注:

  • 1. シングルトン クラスはインスタンスを 1 つだけ持つことができます。

  • 2. シングルトン クラスは独自の一意のインスタンスを作成する必要があります。

  • 3. シングルトン クラスは、このインスタンスを他のすべてのオブジェクトに提供する必要があります。


はじめに

目的: クラスにインスタンスが 1 つだけあることを確認し、そのインスタンスへのグローバル アクセス ポイントを提供します。

主な解決策: グローバルに使用されるクラスは頻繁に作成および破棄されます。

いつ使用するか: インスタンスの数を制御し、システムリソースを節約したい場合。

解決方法: システムにこのシングルトンが既に存在するかどうかを確認し、存在する場合はそれを返し、存在しない場合は作成します。

キーコード: コンストラクターはプライベートです。

適用例: 1. 政党の議長は 1 人だけです。 2. Windows はマルチプロセスおよびマルチスレッドであり、ファイルを操作する場合、複数のプロセスまたはスレッドが同時にファイルを操作することは避けられないため、すべてのファイル処理は固有のインスタンスを通じて実行される必要があります。 3. 一部のデバイス マネージャーはシングルトン モードで設計されていることがよくあります。たとえば、コンピューターに 2 台のプリンターがある場合、出力時に 2 台のプリンターで同じファイルを印刷できないように処理する必要があります。

利点: 1. メモリ内にインスタンスが 1 つだけあるため、特にインスタンスが頻繁に作成および破棄される場合 (経営大学院のホームページのキャッシュなど)、メモリのオーバーヘッドが軽減されます。 2. リソースの複数の占有(ファイルの書き込み操作など)を避けます。

短所: インターフェイスや継承がなく、単一責任の原則と矛盾します。クラスは内部ロジックのみを考慮する必要があり、外部でインスタンスを作成する方法は考慮しません。

使用シナリオ: 1. 固有のシリアル番号の生成をリクエストします。 2. WEB 内のカウンターは、最初に単一インスタンスでキャッシュされるため、更新されるたびにデータベースに追加する必要はありません。 3. 作成されたオブジェクトは、I/O やデータベース接続などのリソースを大量に消費します。

注: 複数のスレッドが同時に入ってインスタンスが複数回インスタンス化されるのを防ぐために、getInstance() メソッドで同期ロック synchronized (Singleton.class) を使用する必要があります。

実装

SingleObjectクラスを作成します。 SingleObject クラスには、プライベート コンストラクターとそれ自体の静的インスタンスがあります。

SingleObject クラスは、外部の世界がその静的インスタンスを取得するための静的メソッドを提供します。 SingletonPatternDemo のデモ クラスは、SingleObject クラスを使用して SingleObject オブジェクトを取得します。

单例模式的 UML 图

ステップ 1

シングルトン クラスを作成します。

SingleObject.java

public class SingleObject {

   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();

   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}

   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return instance;
   }

   public void showMessage(){
      System.out.println("Hello World!");
   }
}

ステップ2

シングルトンクラスから一意のオブジェクトを取得します。

SingletonPatternDemo.java

public class SingletonPatternDemo {
   public static void main(String[] args) {

      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();

      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();

      //显示消息
      object.showMessage();
   }
}

ステップ 3

出力を確認します。

Hello World!

シングルトン パターンを実装するいくつかの方法

シングルトン パターンを実装するには、次のような多くの方法があります:

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、Hungry Chinese style

遅延初期化かどうか: いいえ

マルチスレッドセーフかどうか: はい

実装の難易度: 簡単

説明: このメソッドはより一般的に使用されますが、ガベージ オブジェクトが生成されやすいです。
利点: ロックがないため、実行効率が向上します。
欠点: ロード時にクラスが初期化されるため、メモリが無駄に消費されます。
クラスローダーのメカニズムに基づいてマルチスレッド同期の問題を回避します。ただし、クラスのロードにはさまざまな理由がありますが、そのほとんどはシングルトン モードで 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 つだけであることを確認するために classloder メカニズムも使用します。これは 3 番目のメソッドとは異なります。3 番目のメソッドでは、Singleton クラスがロードされている限り、インスタンスがインスタンス化されます。遅延ロード効果には達しません)、この方法で Singleton クラスがロードされ、インスタンスは必ずしも初期化される必要はありません。 SingletonHolder クラスはアクティブに使用されないため、getInstance メソッドを呼び出してインスタンスをインスタンス化することによってのみ明示的にロードされます。インスタンスのインスタンス化によりリソースが消費されるため、インスタンスを遅延ロードしたい場合を想像してください。一方、Singleton クラスがアクティブに使用される保証がないため、Singleton クラスのロード時にインスタンスをインスタンス化したくない場合を考えてください。他の場所にあり、ロード中である場合、現時点でインスタンスをインスタンス化するのは明らかに不適切です。現時点では、この方法は 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 以降

Lazy 初期化するかどうか: いいえ

マルチにするかどうか- スレッドセーフ: はい

実装の難易度: 簡単

説明: この実装方法は広く採用されていませんが、シングルトン パターンを実装する最良の方法です。これはより簡潔で、シリアル化メカニズムを自動的にサポートし、複数のインスタンス化を完全に防ぎます。
このメソッドは、Effective Java の作者である Josh Bloch によって提唱されています。これは、マルチスレッド同期の問題を回避するだけでなく、シリアル化メカニズムを自動的にサポートし、逆シリアル化による新しいオブジェクトの再作成を防ぎ、複数のインスタンス化を完全に防ぎます。ただし、enum 機能は JDK1.5 以降に追加された機能であるため、このように書くと馴染みがなく、実際の業務で使用されることはほとんどありません。
プライベート コンストラクターは、リフレクション攻撃を通じて呼び出すことはできません。

コード例:

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}


経験: 一般に、1番目と2番目の怠惰な方法を使用することはお勧めできません。3番目の空腹な方法を使用することをお勧めします。 5 番目の登録方法は、遅延読み込み効果が明示的に実装されている場合にのみ使用されます。オブジェクトを作成するために逆シリアル化が必要な場合は、6 番目の列挙方法を試すことができます。他に特別なニーズがある場合は、4 番目の二重チェック ロック方法の使用を検討できます。