JAva 23種類の設計モード——二、単例モード

7112 ワード

ソースコードは私のgithubとgiteeで取得します
紹介する
Singleton Pattern(Singleton Pattern)はJavaの中で最も簡単な設計モードの一つである.このタイプの設計モードは、オブジェクトを作成するための最適な方法を提供する作成モードに属します.このモードは、独自のオブジェクトを作成し、単一のオブジェクトのみが作成されることを保証する単一のクラスに関連します.このクラスは、クラスのオブジェクトをインスタンス化する必要がなく、直接アクセスできる独自のオブジェクトにアクセスする方法を提供します.注意:
  • 1、単一のクラスには1つのインスタンスしかありません.
  • 2、単一のインスタンスクラスは、独自の一意のインスタンスを独自に作成する必要があります.
  • 3、単一のクラスは、このインスタンスを他のすべてのオブジェクトに提供する必要があります.

  • インプリメンテーションモード
    餓漢式単例(静的定数、スレッド安全)
    名前の通り、餓漢式の単例は「空腹」なので、最初から唯一の単例インスタンスを作成しましたが、このインスタンスを使用したことがないとメモリの無駄になります.
    /**
    *      
     *   :  ,            ,         ,    
     *   :             ,               ,         
      */
    public class SingletonTest01 {
        public static void main(String[] args) {
            Signleton instance1= Signleton.getInstance();
            Signleton instance2 = Signleton.getInstance();
            System.out.println(instance1==instance2);
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
        }
    }
    class Signleton{
        //1、      ,      new  
        private Signleton(){ }
        //2、        
        private final static Signleton instance = new Signleton();
        //3、           ,      
        public final static Signleton getInstance(){
            return instance;
        }
    }
    

    出力結果
    true
    1163157884
    1163157884
    

    出力が同じインスタンスであることがわかります
    餓漢式単例(静的コードブロック、スレッドセキュリティ)
    従来の方法と同様に、クラスインスタンス化のプロセスを静的コードブロック、すなわちクラスマウント時に置くだけで、
    静的コードブロック内のコードを実行する場合、メリットとデメリットは以前と同じです.
    /**
     *         ,                    ,         ,
     *             ,        
     */
    public class SingletonTest02 extends Thread{
        public static void main(String[] args) {
            Signleton instance1= Signleton.getInstance();
            Signleton instance2 = Signleton.getInstance();
            System.out.println(instance1==instance2);
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
        }
    }
    class Signleton{
        //1、      ,      new  
        private Signleton(){}
        //2、        
        private static Signleton instance;
    
        static {//      ,      
            instance = new Signleton();
        }
        //3、           ,      
        public final static Signleton getInstance(){
            return instance;
        }
    }
    

    しゅつりょく
    true
    1163157884
    1163157884
    

    怠け者式(スレッドが安全ではない)
    同様に、その名の通り、怠け者式の単例は怠け者だ.インスタンスは、使用するときにのみ作成されます.
    /**
     *    -     
     *   :         ,           
     *        ,         if     ,
     *          ,              ,        (      ),
     *      ,        
     */
    public class SingletonTest03 {
        public static void main(String[] args) {
            for (int i = 0; i <10 ; i++) {
                new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
            }
        }
    }
    class Signleton{
        private static Signleton instance;
        private Signleton(){}
        //           ,      ,    instance
        public static Signleton getInstance(){
            if(instance == null){//          
                instance = new Signleton();
            }
            return instance;
        }
    }
    
    

    しゅつりょく
    546405844
    135417039
    135417039
    802181073
    135417039
    135417039
    135417039
    802181073
    135417039
    135417039
    

    ここでは極端な状況を選びました.もしあなたのパソコンの構成がよければ、何度も実行した結果、単一のモードに合っている可能性があります.
    怠け者式(同期方法、スレッドセキュリティ)
    上記の方法でスレッドが安全でない場合があるのは、マルチスレッドの場合、単一の例が作成されたかどうかを同時に判断する複数のスレッドがある可能性があるからです.ではこの問題を解決するにはgetInstance()メソッドを同期するだけです
    /**
     *            
     *                       ,  getInstance()        
     */
    public class SingletonTest04 {
        public static void main(String[] args) {
            for (int i = 0; i <10 ; i++) {
                new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
            }
        }
    }
    
    class Signleton{
        private static Signleton instance;
    
        private Signleton(){}
    
        //           ,      ,    instance
        public static synchronized Signleton getInstance(){
            if(instance == null){//          
                instance = new Signleton();
            }
            return instance;
        }
    }
    

    結果
    802181073
    802181073
    802181073
    802181073
    802181073
    802181073
    802181073
    802181073
    802181073
    802181073
    

    しかし、synchronizedは非常に重い同期ロックであり、getInstance()を実行するたびに同期し、効率に極めて影響します.
    怠け者式(ダブルチェック、スレッドセキュリティ)
    二重検査錠は、二重検査錠とも呼ばれ、怠け者式と餓漢式の両者の長所と短所を統合したものである.上記のコード実装を見ると、synchronizedキーワードの内外にif条件判断が施されているのが特徴で、スレッドの安全を保証するとともに、直接ロックよりも実行効率が向上し、メモリスペースも節約できます.
    /**
     *     -    
     *      if    ,          
     *         ,             
     */
    public class SingletonTest05 {
        public static void main(String[] args) {
            for (int i = 0; i <10 ; i++) {
                new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
            }
        }
    }
    
    class Signleton{
        private static volatile Signleton instance;//volatile     
        private Signleton(){}
        //           ,        ,        ,         
        public static Signleton getInstance() {
            if (instance == null) {
                synchronized (Signleton.class) {
                    if (instance == null) {
                        instance = new Signleton();
                    }
                }
            }
            return instance;
        }
    }
    

    実行結果
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    

    推奨
    静的内部クラス(スレッドセキュリティ)
    /**
     *            
     *                           
     *       Signleton             ,        ,    SignletonInstance 
     *                      
     *         ,            ,   
     */
    public class SingletonTest07 {
        public static void main(String[] args) {
            for (int i = 0; i <10 ; i++) {
                new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
            }
        }
    }
    class Signleton{
        //     
        private Signleton(){}
        //     ,          Signleton
        private static class SignletonInstance{
            private static final Signleton instance = new Signleton();
        }
        //           ,    SignletonInstance.instance
        public static Signleton getInstance() {
            return SignletonInstance.instance;
        }
    }
    
    

    結果
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    79372097
    

    この方式は比較的簡単で,使用を推奨する
    列挙(スレッドセキュリティ)
    /**
     * @author codermy
     * @createTime 2020/5/14
     *           
     *   jdk1.5              ,
     *             ,                 
     */
    public class SingletonTest08 {
        public static void main(String[] args) {
            Singleton singleton = Singleton.INSTANCE;
            singleton.Ok();
            for (int i = 0; i <10 ; i++) {
                new Thread(() -> System.out.println(Singleton.INSTANCE.hashCode()) ).start();
            }
    
        }
    }
    enum Singleton{
        INSTANCE;//  
        public void Ok(){
            System.out.println("ok");
        }
    }
    

    結果
    ok
    858497792
    858497792
    858497792
    858497792
    858497792
    858497792
    858497792
    858497792
    858497792
    858497792
    

    列挙は単例モードを実現し,最も簡潔で推奨されていることがわかる.しかし、それが簡潔であるため、可読性が悪い.