JAVA単例パターンまとめ

5293 ワード

単一モード
定義:クラスにインスタンスが1つしかないことを確認し、独自にインスタンス化し、システム全体にこのインスタンスを提供したいと考えています.
シーンの使用:クラスが複数のオブジェクトを生成してリソースを消費しすぎることを回避し、クラスがプログラムに1つのインスタンスしかないことを確認します.
単一モードの利点:
  • 頻繁に使用されるオブジェクトについては、new操作にかかる時間を省略することができ、これはそれらの重量級オブジェクトにとって非常に大きなシステムオーバーヘッドである.
  • new操作の回数が減少するため、システムメモリの使用頻度も低下し、GCの圧力を軽減し、GCの停止時間を短縮する.

  • シングルインプリメンテーション方式
    方法一(餓漢式):
    public class Singleton {
        private Singleton(){
        }
    
        private static Singleton singleton = new Singleton();
    
        public Singleton getInstance() {
            return singleton;
        }
    }

    説明:単純で正確ですが、Singletonインスタンスの作成タイミングは制御されません.Singletonメソッドまたはフィールドへの参照はクラスの初期化とinstanceインスタンスの作成をもたらしますが、クラスの初期化は1回しかないので、instanceインスタンスは永遠に1回しか作成されません.しかし、プログラムが最初からこのインスタンスを使用する必要がない場合、インスタンスのオブジェクトは作成されます.これは不要な資源の浪費をもたらした.
    方法二(餓漢式):
    public class LazySingleton {
        private LazySingleton(){
        }
    
        private static LazySingleton instance = null;
    
        public static synchronized LazySingleton getInstance() {
            if (null == instance) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }

    説明:同時シーンでのパフォーマンス損失
    方法三(二重ロックDCLの実現方式、推奨しない):
    public class LazySingleton {
        private static LazySingleton instance = null;
    
        private LazySingleton() {
        }
    
        public static LazySingleton getInstance() {
            if (null == instance) {
                synchronized (LazySingleton.class) {
                    if (null == instance) {//  volatile instance         
                        instance = new LazySingleton();
                    }
                }
            }
            return instance;
        }
    }

    このコードには1つの問題しかありません.正常に動作しません(「悪名高い二重ロック検査」とも呼ばれています).最も明らかな理由は、インスタンスを初期化する書き込み操作とインスタンスフィールドの書き込み操作がコンパイラまたはバッファによって並べ替えられ、並べ替えが部分的に構成されたものを返す可能性があるためである.初期化されていないオブジェクトを読み込みましたこのコードには他にも多くのエラーがあり、なぜこのコードのアルゴリズム修正が間違っているのか.古いjavaメモリモデルでは修復できません
    「多くの人がvolatileキーワードを使用すると、二重ロックチェックモードの問題を解消できると考えています.1.5のJVMより前に、volatileはこのコードが正常に動作することを保証することはできません.(環境によって異なります).新しいメモリモデルの下で、インスタンスフィールドはvolatileを使用して二重ロックチェックの問題を解決します.スレッドを構築していくつかのものを初期化し、スレッドがその値を返す間にhappens-before関係があるためです.
    そして、二重ロックチェックが好きな人にとっては(私たちは本当に誰もそうしないことを望んでいます)まだ良いメッセージではありません.二重ロックチェックのポイントは、同期の過剰使用によるパフォーマンスの問題を回避することです.java 1.0から、同期には高価なパフォーマンスオーバーヘッドがあるだけでなく、新しいメモリモデルではvolatileを使用するパフォーマンスオーバーヘッドも上昇し、同期と同じパフォーマンスオーバーヘッドにほぼ達します.そのため、二重ロック検査を用いて単一のモードを実現することは依然として良い選択ではない.(リビジョン-ほとんどのプラットフォームではvolatileのパフォーマンスオーバーヘッドが低い)http://ifeve.com/jmm-faq-dcl/》
    方法四(推奨):
    Initialization Demand Holder(IoDH)実装の一例
    public class StaticSingleton {
        private StaticSingleton(){
        }
    
        private static class StaticSingletonHandler {
    
            public static StaticSingleton instance = new StaticSingleton();
        }
    
        public static StaticSingleton getInstance() {
            return StaticSingletonHandler.instance;
        }
    }

    静的単一例オブジェクトはSingletonのメンバー変数として直接インスタンス化されていないため、クラスのロード時にSingletonはインスタンス化されず、getInstance()を初めて呼び出すと内部クラスHolderClassがロードされ、この内部クラスにstaticタイプの変数instanceが定義され、このメンバー変数が最初に初期化され、Java仮想マシンによってスレッドの安全性が保証される.メンバー変数が一度だけ初期化できることを確認します.getInstance()メソッドにはスレッドロックがないため、パフォーマンスに影響はありません.
    IoDHを使用することによって、遅延ロードを実現することができ、またスレッドの安全を保証することができ、システムの性能に影響を与えず、最高のJava言語の単例モードの実現方式であることを失わない.
    利点:1.getInstance()メソッドにはロックがなく、高同時シーンではパフォーマンスが優れています.2.getInstance()メソッドが呼び出されると、StaticSingletonインスタンスが初期化されます.
    参考資料http://ifeve.com/jmm-faq-dcl