設計モードの単例モード経典総括(実現方式、応用シーン、優劣、注意事項)


ネット上で人気のあるブログをいくつか参考にしました.統合を行い、主に2つの部分に分けられ、第1の大部分は詳しく例を挙げて紹介し(生き生きと理解)、第2の部分は経典の総括(面接に適している)である.
一、詳しく紹介する.
概念:javaにおける単例モードはよく見られる設計モードであり、単例モードの書き方はいくつかあり、ここでは主に3種類を紹介する:怠け者式単例、餓漢式単例、登録式単例.シングル・インスタンス・モードには、1、シングル・インスタンス・クラスに1つのインスタンスしかないという特徴があります.2、単一のインスタンスクラスは、独自の一意のインスタンスを作成する必要があります.3、単一のクラスは、他のすべてのオブジェクトにこのインスタンスを提供する必要があります.単一のインスタンス・モードは、クラスに1つのインスタンスしかなく、独自にインスタンス化され、システム全体にこのインスタンスが提供されることを保証します.コンピュータシステムでは、スレッドプール、キャッシュ、ログオブジェクト、ダイアログボックス、プリンタ、グラフィックスカードのドライバオブジェクトが単一の例として設計されることが多い.これらのアプリケーションは、リソースマネージャの機能を多かれ少なかれ備えています.各コンピュータには複数のプリンタがありますが、2つの印刷ジョブが同時にプリンタに出力されないように、Printer Spoolerは1つしかありません.各コンピュータにはいくつかの通信ポートがあり、システムは、1つの通信ポートが同時に2つの要求によって同時に呼び出されないように、これらの通信ポートを集中的に管理しなければならない.要するに、単例モデルを選んだのは、不一致状態を避け、政出多頭を避けるためだ.
一、怠け者式単例
//      .               
public class Singleton {
    private Singleton() {}
    private static Singleton single=null;
    //       
    public static Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
    }
}

Singletonは、構築方法をprivateに限定することで、クラスが外部でインスタンス化されることを回避し、同じ仮想マシンの範囲内でSingletonの唯一のインスタンスはgetInstance()メソッドでしかアクセスできません.
(実際、Java反射メカニズムによって構造方法がprivateのクラスをインスタンス化することができ、それは基本的にすべてのJava単例を失効させる.この問題はここでは議論せず、反射メカニズムは存在しないと一応耳を隠している.)
しかし、以上の怠け者の単例の実現はスレッドの安全問題を考慮していない.それはスレッドが安全ではない.同時環境では複数のSingletonインスタンスが現れる可能性が高い.スレッドの安全を実現するには、以下の3つの方法があり、getInstanceという方法を改造し、怠け者の単例のスレッドの安全を保証している.スレッドの安全についてよく知らないので、まず次の3つの小さなバーをスキップして、餓漢式の単例を見に行って、見終わってからスレッドの安全の問題を振り返って考えることができます.
1.getInstanceメソッドに同期を付ける
public static synchronized Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
}

2、二重検査ロック
public static Singleton getInstance() {
        if (singleton == null) {  
            synchronized (Singleton.class) {  
               if (singleton == null) {  
                  singleton = new Singleton(); 
               }  
            }  
        }  
        return singleton; 
    }

3、静的内部クラス
public class Singleton {  
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  

これは、上記の1、2よりも優れており、スレッドのセキュリティを実現し、同期によるパフォーマンスの影響を回避します.
二、餓漢式単例
//      .      ,        
public class Singleton1 {
    private Singleton1() {}
    private static final Singleton1 single = new Singleton1();
    //       
    public static Singleton1 getInstance() {
        return single;
    }
}

餓漢式はクラスの作成と同時に静的なオブジェクトをシステムで使用するために作成したので、後で変更しないので、生まれつきスレッドが安全です.
三、登録式単例(無視可能)
//  Spring     ,     ,         。
public class Singleton3 {
    private static Map map = new HashMap();
    static{
        Singleton3 single = new Singleton3();
        map.put(single.getClass().getName(), single);
    }
    //        
    protected Singleton3(){}
    //      ,         
    public static Singleton3 getInstance(String name) {
        if(name == null) {
            name = Singleton3.class.getName();
            System.out.println("name == null"+"--->name="+name);
        }
        if(map.get(name) == null) {
            try {
                map.put(name, (Singleton3) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
    //          
    public String about() {    
        return "Hello, I am RegSingleton.";    
    }    
    public static void main(String[] args) {
        Singleton3 single3 = Singleton3.getInstance(null);
        System.out.println(single3.about());
    }
}

登録式の一例は、実際には一例クラスのインスタンスのセットを維持し、これらのインスタンスを1つのMap(登録帳)に格納し、登録されたインスタンスについてはMapから直接戻り、登録されていない場合は先に登録し、その後に戻る.
ここでは登録式の単例には無視できますが、私の理解では、まずそれが使われているのは少ないです.また、内部実装はやはり餓漢式の単例を使っています.その中のstaticメソッドブロックのため、その単例はクラスがロードされたときに実例化されています.
餓漢式と怠け者式の違い
名前から言えば、餓漢と怠け者、
餓漢はクラスがロードされると、単例の初期化が完了し、getInstanceが保証されると、単例はすでに存在します.
怠け者は怠け者で、getInstanceを呼び出すときだけ、この単例を初期化します.
また、次の2つの方法を区別します.
1、スレッドセキュリティ:
餓漢式は生まれながらにしてスレッドが安全で、問題なくマルチスレッドに直接使用することができます.
怠け者式自体は非スレッドセキュリティであり、スレッドセキュリティを実現するためにいくつかの書き方があり、それぞれ上の1、2、3であり、この3つの実現はリソースロードと性能の面でいくつかの違いがある.
2、リソースのロードとパフォーマンス:
餓漢式はクラスの作成と同時に静的オブジェクトをインスタンス化し、その後この単一の例を使用するかどうかにかかわらず、一定のメモリを占有しますが、それに応じて、リソースの初期化が完了したため、最初の呼び出しでも速度が速くなります.
一方、怠け者式はその名の通りロードが遅延し、この単例を初めて使用したときにオブジェクトがインスタンス化され、最初の呼び出し時に初期化され、やるべき仕事が多ければ性能的に遅延し、その後は餓漢式と同じになる.
1、2、3の3つの実現にはいくつかの違いがあります.
1つ目は、メソッド呼び出しに同期を加えたもので、スレッドは安全ですが、毎回同期しなければならず、パフォーマンスに影響します.99%の場合は同期する必要はありません.
2つ目は、getInstanceでnullチェックを2回行い、1回目の呼び出し例のみが同期することを確保することで、スレッドが安全であると同時に、毎回同期する性能損失を回避
3つ目はclassloaderのメカニズムを利用してinstanceの初期化時に1つのスレッドしかないことを保証しているので、スレッドも安全であり、性能損失もないので、一般的にはこれを使用する傾向があります.
二、経典の総括
実装方法:
a)実装されたクラスの構築方法をprivateとして設計する.
b)このような参照の静的メンバー変数を追加し、実例化する.
c)実装されたクラスに共通のCreateInstance関数を提供し,インスタンス化されたクラス,すなわちbの静的メンバ変数を返す.
メリットとデメリット:
利点:1.単一インスタンスモードでは、アクティブな単一インスタンスは1つのインスタンスしかなく、単一インスタンスクラスのすべてのインスタンス化に対して同じインスタンスが得られます.これにより、他のオブジェクトが自分のインスタンス化を防止し、すべてのオブジェクトがインスタンス2にアクセスすることを確保する.単一のインスタンスモードは一定の伸縮性を有し、クラス自身がインスタンス化プロセスを制御し、クラスはインスタンス化プロセスを変更する上で相応の伸縮性を有する.3.一意のインスタンスへの管理されたアクセスを提供します.4.システムメモリに1つのオブジェクトしか存在しないため、システムリソースを節約でき、頻繁に作成および破棄するオブジェクトが必要な場合、単一のモードでシステムのパフォーマンスを向上させることは間違いありません.5.可変数のインスタンスを許可します.6.共有リソースの多重占有を避ける.欠点:1.変化するオブジェクトには適用されません.同じタイプのオブジェクトが常に異なるインスタンスシーンで変化する場合、単一のインスタンスはデータのエラーを引き起こし、互いの状態を保存できません.2.単利モードには抽象層がないため,単例クラスの拡張が困難である.3.単例類の職責が重すぎて、ある程度「単一職責原則」に背いている.4.単一インスタンスを悪用すると、リソースを節約するためにデータベース接続プールオブジェクトを設計した単一インスタンスクラスのような負の問題が発生し、接続プールオブジェクトを共有するプログラムが多すぎて接続プールがオーバーフローする可能性があります.インスタンス化されたオブジェクトが長時間利用されない場合、システムはゴミとして回収され、オブジェクトの状態が失われます.
注意事項:
1.              ,             
2.                 
3.                   ,         ,           (      ) 

適用シーン:単一のモードでは1つのオブジェクトのみを作成できます.そのため、メモリを節約し、オブジェクトへのアクセスを高速化するため、複数のモジュールが同じデータソースでオブジェクトを接続するなど、オブジェクトが共通の場合に適している必要があります.例:1.頻繁にインスタンス化して破棄するオブジェクトが必要です.2.オブジェクトの作成に時間がかかりすぎたり、リソースがかかりすぎたりしますが、よく使用されるオブジェクトです.3.ステータスのあるツールクラスオブジェクト.4.データベースまたはファイルに頻繁にアクセスするオブジェクト.以下は、単一のモードの古典的な使用シーンです.1.リソース共有の場合、リソース操作によるパフォーマンスや損失などを回避します.上記のログファイルのように、構成を適用します.2.制御リソースの場合、リソース間の相互通信を容易にする.スレッドプールなどです.適用シーンの例:1.外部リソース:各コンピュータには複数のプリンタがありますが、PrinterSpoolerは1つしかありません.2つの印刷ジョブが同時にプリンタに出力されないようにします.内部リソース:ほとんどのソフトウェアには1つ(または複数)のプロパティファイルがシステム構成を格納しています.このようなシステムには、1つのオブジェクトがこれらのプロパティファイルを管理する必要があります.2.WindowsのTaskManager(タスクマネージャ)は典型的な単一のモードです.(これはよく知っているでしょう)、考えてみてください.そうではありませんか.windows task managerを2つ開けてもいいですか.自分でやってみてください.3.windowsのRecycle Bin(ごみ箱)も典型的な一例応用である.システム全体の運行過程において、ごみ箱はずっと唯一の実例を維持している.4.ウェブサイトのカウンタは、一般的に一例モードで実現されているが、そうでなければ同期しにくい.5.アプリケーションのログ応用は、一般的に一例モードで実現されているが、これは一般的に共有されたログファイルがずっとオープン状態にあるためである.1つのインスタンスしか操作できないので、内容が追加されにくいです.6.Webアプリケーションのコンフィギュレーションオブジェクトの読み取りは、コンフィギュレーションファイルが共有リソースであるため、一般的には単一のモードも適用される.7.データベース接続プールの設計は、データベース接続がデータベースリソースであるため、一般的には単一のモードを採用します.データベース・ソフトウェア・システムでは、主にデータベース接続プールを使用して、データベース接続を開いたり閉じたりすることによる効率損失を節約します.この効率的な損失は、単一のモードで維持することで、この損失を大幅に低減することができます.8.マルチスレッドのスレッドプールの設計も一般的に単例モードを採用している.これは、スレッドプールがプール内のスレッドを制御するのに便利であるからである.9.オペレーティングシステムのファイルシステムは、大きな単例モード実現の具体例でもあり、1つのオペレーティングシステムには1つのファイルシステムしかない.10.HttpApplicationも単位例の典型的な応用である.ASPに詳しい.Net(IIS)の要求ライフサイクル全体の人は、HttpApplicationも単一のモードであり、すべてのHttpModuleが1つのHttpApplicationインスタンスを共有していることを知っているはずだ.
参照先:https://blog.csdn.net/dean_hu/article/details/71195133 https://blog.csdn.net/jason0539/article/details/23297037