CodeGym/Java Blog/ランダム/Javaシングルトンクラス
John Squirrels
レベル 41
San Francisco

Javaシングルトンクラス

ランダム グループに公開済み
人のメンバー
やあ!今日は、Java シングルトン パターンから始めて、さまざまな設計パターンの詳細を見ていきます。復習してみましょう: デザイン パターン一般について何を知っていますか? デザイン パターンは、多くの既知の問題を解決するために適用できるベスト プラクティスです。デザイン パターンは通常、どのプログラミング言語にも関連付けられていません。これらは、間違いを避け、車輪の再発明を避けるための一連の推奨事項と考えてください。デザインパターン:シングルトン - 1

Javaのシングルトンとは何ですか?

シングルトンは、最も単純なクラスレベルの設計パターンの 1 つです。時々、「このクラスはシングルトンです」と言われることがあります。これは、クラスがシングルトン設計パターンを実装していることを意味します。場合によっては、インスタンス化を単一のオブジェクトに制限するクラスを作成する必要があります。たとえば、ロギングや接続を担当するクラスなどです。シングルトン デザイン パターンは、これを実現する方法を説明します。シングルトンは、次の 2 つのことを行うデザイン パターンです。
  1. これにより、クラスのインスタンスが 1 つだけ存在することが保証されます。

  2. これにより、そのインスタンスへのグローバル アクセスの単一ポイントが提供されます。

したがって、シングルトン パターンのほぼすべての実装に特徴的な 2 つの機能があります。
  1. プライベートコンストラクター。これにより、クラス自体の外部でクラスのオブジェクトを作成する機能が制限されます。

  2. クラスのインスタンスを返すパブリック静的メソッド。このメソッドはgetInstanceと呼ばれます。これは、クラス インスタンスへのグローバル アクセスのポイントです。

実装オプション

シングルトンデザインパターンはさまざまな方法で適用されます。それぞれのオプションには、それぞれの意味で良い面と悪い面があります。いつものように、ここでも完璧な選択肢はありませんが、私たちは 1 つを目指して努力する必要があります。まず最初に、何が良いか悪いかを判断し、デザイン パターンのさまざまな実装を評価する方法にどのような指標が影響するかを決定しましょう。良いところから始めましょう。実装をより効果的で魅力的なものにする要因は次のとおりです。
  • 遅延初期化: インスタンスは必要になるまで作成されません。

  • シンプルで透過的なコード: この指標はもちろん主観的なものですが、重要です。

  • スレッド セーフ: マルチスレッド環境での正しい動作。

  • マルチスレッド環境での高いパフォーマンス: リソースの共有時にスレッドがブロックされることがほとんど、またはまったくありません。

次に短所です。実装に悪影響を与える要因をリストします。
  • 遅延初期化なし: 必要かどうかに関係なく、アプリケーションの起動時にクラスがロードされるとき (逆説的ですが、IT の世界では遅延するほうが良いのです)

  • 複雑で読みにくいコード。この指標も主観的なものです。目が出血し始めた場合は、実装が最適ではないとみなします。

  • スレッドの安全性の欠如。つまり「スレの危険」。マルチスレッド環境での誤った操作。

  • マルチスレッド環境でのパフォーマンスの低下: リソースを共有するときにスレッドが常に、または頻繁に相互にブロックします。

コード

これで、さまざまな実装オプションを検討し、長所と短所を示す準備が整いました。

単純

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
最も単純な実装。長所:
  • シンプルで透明なコード

  • スレッドの安全性

  • マルチスレッド環境での高いパフォーマンス

短所:
  • 遅延初期化はありません。
前述の欠点を修正するために、実装番号 2 を取得します。

遅延初期化

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
長所:
  • 遅延初期化。

短所:
  • スレッドセーフではありません

この実装は興味深いですね。遅延初期化は可能ですが、スレッドの安全性が失われます。心配はいりません。実装 3 ではすべてを同期します。

同期アクセス

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
長所:
  • 遅延初期化。

  • スレッドの安全性

短所:
  • マルチスレッドのパフォーマンスが低い

素晴らしい!実装その 3 では、スレッドの安全性を回復します。もちろん、遅いです... getInstanceメソッドは同期されるため、一度に 1 つのスレッドのみで実行できます。実際には、メソッド全体を同期するのではなく、新しいインスタンスを初期化する部分のみを同期する必要があります。しかし、単純に同期ブロックを使用して、新しいインスタンスの作成を担当する部分をラップすることはできません。それを行うと、スレッドの安全性は保証されません。すべてはもう少し複雑です。適切な同期は以下で確認できます。

二重チェックロック

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 バージョン以降で修正されています)

この実装オプションが正しく機能するには、2 つの条件のうち 1 つが満たされる必要があることに注意してください。INSTANCE変数はfinalまたはvolatileでなければなりません。今日説明する最後の実装は、クラス ホルダー 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;
   }
}
長所:
  • 遅延初期化。

  • スレッドの安全性。

  • マルチスレッド環境での高いパフォーマンス。

短所:
  • 正しく動作するには、シングルトンオブジェクトがエラーなく初期化されることが保証される必要があります。それ以外の場合、 getInstanceメソッドへの最初の呼び出しでExceptionInInitializerErrorが発生し、後続のすべての呼び出しで NoClassDefFoundError が生成されます

この実装はほぼ完璧です。遅延があり、スレッドセーフで高速です。ただし、短所のリストで説明したように、これにはニュアンスがあります。 シングルトン パターンのさまざまな実装の比較:
実装 遅延初期化 スレッドの安全性 マルチスレッドのパフォーマンス いつ使用しますか?
単純 - + 速い 一度もない。あるいは、遅延初期化が重要ではない場合も考えられます。しかし、これ以上良いことはありません。
遅延初期化 + - 適用できない マルチスレッドが必要ないときは常に
同期アクセス + + 遅い 一度もない。あるいは、マルチスレッドのパフォーマンスが重要ではない場合も考えられます。しかし、これ以上良いことはありません。
二重チェックロック + + 速い まれに、シングルトンの作成時に例外を処理する必要がある場合 (クラス ホルダーのシングルトンが適用できない場合)
クラスホルダー + + 速い マルチスレッドが必要で、シングルトン オブジェクトが問題なく作成されることが保証されている場合。

シングルトン パターンの長所と短所

一般に、シングルトンは期待どおりのことを行います。
  1. これにより、クラスのインスタンスが 1 つだけ存在することが保証されます。

  2. これにより、そのインスタンスへのグローバル アクセスの単一ポイントが提供されます。

ただし、このパターンには次のような欠点があります。
  1. シングルトンは単一責任の原則に違反します。シングルトン クラスは、その直接の役割に加えて、インスタンスの数も制御します。

  2. 通常のクラスのシングルトンへの依存は、クラスのパブリック コントラクトには表示されません。

  3. グローバル変数はダメです。最終的に、シングルトンは巨大なグローバル変数に変わります。

  4. シングルトンが存在すると、アプリケーション全体、特にシングルトンを使用するクラスのテスト容易性が低下します。

以上です!:) 私たちは皆さんと一緒に Java シングルトン クラスについて調べてきました。これからは、プログラマーの友人と会話するときに、そのパターンがいかに優れているかだけでなく、そのパターンの悪さについても一言言及することができるようになります。この新しい知識をマスターして頑張ってください。

追加の資料:

コメント
  • 人気
  • 新規
  • 古い
コメントを残すには、サインインしている必要があります
このページにはまだコメントがありません