シングルトンを壊さないでください! Java で % スレッドセーフにする方法は次のとおりです

Patricia Arquette
リリース: 2024-11-02 09:55:02
オリジナル
385 人が閲覧しました

Don’t Let Your Singleton Break! Here’s How to Make It % Thread-Safe in Java

この投稿では、積極的な初期化二重チェック ロック、および 内部静的クラス アプローチ。シングルトンの整合性を確保する上で、final キーワードがなぜ有益なのかについても説明します。

シングルトンを使用する理由

シングルトンは、アプリケーション全体でクラスの

1 つのインスタンスが必要な場合に便利です。一般的な使用例には、ロギング、構成、接続プールなどの共有リソースの管理が含まれます。シングルトンは、クラスにアクセスする複数のリクエストが、新しいインスタンスを作成するのではなく、確実に同じインスタンスを共有するようにします。

1. 積極的な初期化: 最も単純なシングルトン

積極的な初期化パターンは、クラスがロードされるときにシングルトン インスタンスを作成します。これは簡単で、クラスが JVM によってロードされるときにインスタンスが作成されるため、スレッドの安全性が確保されます。


public final class Singleton {
    // Instance is created at class loading time
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
ログイン後にコピー
ログイン後にコピー
Eager Initialization の長所と短所

長所:

    JVM はクラスの初期化がスレッドセーフであることを保証するため、デフォルトでシンプルかつスレッドセーフです。
  • 同期や追加の複雑さは必要ありません。

短所:

    インスタンスは使用されるかどうかに関係なく作成されるため、シングルトンがまったく必要ない場合はリソースが無駄になる可能性があります。

いつ使用するか: シングルトン クラスが軽量で、アプリケーションの実行時に使用されることが確実な場合は、即時初期化が最適です。


2. 二重チェックされたロックによる遅延初期化

必要になるまでシングルトンの作成を遅らせたい場合 (

遅延初期化 と呼ばれる) には、二重チェックされたロックがスレッドセーフなソリューションを提供します。最小限の同期を使用し、初めてアクセスしたときにのみインスタンスが作成されるようにします。

public final class Singleton {  // Marked as final to prevent subclassing

    // volatile ensures visibility and prevents instruction reordering
    private static volatile Singleton instance;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {               // First check (no locking)
            synchronized (Singleton.class) {   // Locking
                if (instance == null) {        // Second check (with locking)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
ログイン後にコピー
二重チェックロックが機能する理由

  1. 最初のチェック: 同期ブロックの外側の if (instance == null) チェックにより、getInstance() が呼び出されるたびにロックすることを回避できます。これにより、初期化後の将来の呼び出しに対して同期ブロックがバイパスされるため、パフォーマンスが向上します。

  2. 同期ブロック: インスタンスが null になると、同期ブロックに入ると、1 つのスレッドのみがシングルトン インスタンスを作成するようになります。このポイントに到達する他のスレッドは待機する必要があり、競合状態が防止されます。

  3. 2 番目のチェック: 同期ブロック内で、インスタンスを再度チェックして、現在のスレッドが待機している間に他のスレッドがインスタンスを初期化していないことを確認します。この二重チェックにより、シングルトン インスタンスが 1 つだけ作成されることが保証されます。

なぜ揮発性が必要なのか

volatile キーワードは、命令の並べ替えを防ぐために、二重チェックされたロック パターンに不可欠です。これがないと、ステートメント instance = new Singleton(); になります。完全に初期化される前に他のスレッドに対して完了したように見える可能性があり、部分的に構築されたインスタンスが返されることになります。 volatile は、インスタンスが null でなくなると、完全に構築され、すべてのスレッドに表示されることを保証します。

ファイナルがグッドプラクティスである理由

ここでは、サブクラス化を防ぐために、final キーワードが使用されています。 Singleton クラスを Final としてマークすると、次の 2 つの主な利点があります。

  1. サブクラス化の防止: クラスを Final にすることで、他のクラスがそのクラスを拡張できなくなります。これにより、サブクラス化により追加のインスタンスが生成され、シングルトン パターンが崩れる可能性があるため、シングルトン クラスのインスタンスは 1 つだけ存在できることが保証されます。

  2. Signals Immutability: Final は、シングルトン クラスが不変であることを意図しており、拡張すべきではないことを他の開発者に明確に示すものとして機能します。これにより、コードの理解と保守が容易になります。

つまり、final はシングルトンの整合性を強化し、サブクラス化による予期せぬ動作を回避するのに役立ちます。

二重チェックロックの長所と短所

長所:

  • 遅延初期化は、必要になるまで作成を遅らせてリソースを節約します。
  • 二重チェックによる同期オーバーヘッドが最小限に抑えられます。

短所:

  • 少し複雑で、安全のために揮発性が必要です。
  • 積極的な初期化で十分な、より単純なシングルトンのニーズには過剰になる可能性があります。

使用する場合: このパターンは、シングルトン クラスがリソースを大量に消費し、常に必要になるとは限らない場合、またはマルチスレッド環境でのパフォーマンスが懸念される場合に役立ちます。


3. 内部静的クラス: よりクリーンな遅延初期化の代替手段

遅延初期化に対する代替のスレッドセーフなアプローチは、内部静的クラス パターンです。これは、Java のクラスローディング メカニズムを利用して、明示的な同期を行わずに、必要な場合にのみシングルトン インスタンスを初期化します。

public final class Singleton {
    // Instance is created at class loading time
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
ログイン後にコピー
ログイン後にコピー

内部静的クラスの仕組み

このパターンでは、SingletonHelper クラスは getInstance() が初めて呼び出されたときにのみロードされます。これにより INSTANCE の初期化がトリガーされ、同期されたブロックを必要とせずに遅延読み込みが保証されます。

内部静的クラスの長所と短所

長所:

  • 揮発性ブロックや同期ブロックを必要としないスレッドセーフです。
  • シンプルかつクリーンで、設計により遅延初期化が行われます。

短所:

  • Java のクラスローディングの仕組みに慣れていない新規開発者にとっては、若干直感的ではありません。

いつ使用するか: クリーンで保守可能なコードによる遅延初期化が必要な場合は、内部静的クラス パターンを使用します。シンプルさとスレッドセーフのため、多くの場合、これが好まれる選択です。


まとめ

Java でスレッドセーフなシングルトンを実装するための 3 つの一般的な方法を検討してきました。

  1. 積極的な初期化: シングルトンが軽量で常に使用される単純なケースに最適です。
  2. 二重チェックされたロック: 遅延初期化が必要な、パフォーマンス重視のマルチスレッド環境に最適です。
  3. 内部静的クラス: Java のクラスロード動作を使用した遅延初期化へのクリーンでスレッドセーフなアプローチ。

それぞれのアプローチには長所があり、さまざまなシナリオに適しています。ご自身のプロジェクトで試してみて、どれが最適かを確認してください。ご希望のアプローチやご質問がございましたら、コメント欄でお知らせください。

コーディングを楽しんでください! ?‍??‍?

以上がシングルトンを壊さないでください! Java で % スレッドセーフにする方法は次のとおりですの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート