淺談23種設計模式(單例模式)

淺談23種設計模式(java版)

單例模式


  • 什麼是設計模式

    • 設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類的、代碼設計經驗的總結,使用設計模式的目的是爲了提高代碼的可重用性,使得代碼更加符合人類的理解思維。
  • 什麼是單例模式

    • 單例顧名思義在整個系統中只有一個類的實例對象。
  • 這有什麼好處

    • 有一些對象我們確實只需要一個,例如,線程池,緩存,對話框,日誌對象等等,就只能有一個實例,如果創建多個實例,就會導致許多問題,例如程序運行異常,運行結果不一致等問題。
  • 不簡單的單例模式

    • 雖然單例模式聽起來簡單易懂,但是真正想實現一個單例模式也不是那麼容易的,如何保證一個對象只能實例化一次,如何保證多線程下的線程安全問題等等。
  • 接下來讓我們一步一步的來實現單例模式

    • 如何創建一個對象?

      new Singleton();

    • 如果其它對象想創建Singleton會怎麼樣? 可以再次創建嗎?

      是的,當然可以

    • 如果有一個類我們是否可以多次實例化它?

      答案是:當然可以了, 只要你的類是公開的

    • 如果不公開類會怎麼樣?

      如果不公開類,那只有同一個包內的類可以實例化它,並可以實例化多次(小夥子你好摳)

    • 我們可以讓外部無法實例化它嗎?

      當然可以了,如果我們把它的構造方器定義爲私有的,除了它的內部類可以初始化它,其它對象都不能初始化它,當然也就不能實例化它(讓人活不了)

        public Singleton{
            private Singleton(){ }
        }
      
    • 我覺得私有構造器是不是有點不合理?

      沒錯確實有點不合理,因爲我們必須有Singleton類的實例纔可以調用該類的構造器,但沒有類能實例化它,所以我們得不到它的實例,這就好比 “雞生蛋,蛋生雞”,我們只能在類內部調用構造器。

    • 我有個想法你認爲如何?

      如果我們在類的內部創建一個靜態方法去調用它如何。

       public Singleton{
            private Singleton(){ }
            public static Singleton getInstance(){
            }
       }
      
    • 爲什麼 調用getInstance() 不用對象名而用類名?

      因爲getInstance()方法是靜態的,靜態方法是屬於類而非屬於對象的,所以調用用它只能用類名。

    • 有意思,如果把上面的組合在一起會如何?

      我們就能夠在getInstance()方法中愉快的進行實例的創建了。

      public Singleton{
           private Singleton(){ }
           public static Singleton getInstance(){
               return new Singleton();
         }
      } 
      
    • 我們是否解決了實例化問題?

      對,我們已經可以實例化對象了,但也僅僅是實例化對象,並沒有保證對象唯一。

    • 如何保證對象的唯一性?

      我們改造一下上面的代碼

       //懶漢模式 
       public Singleton{
            //保存實例化的對象
            private static Singleton  singleton = null;
            private Singleton(){ }
            public static Singleton getInstance(){
               if(singleton == null){
                    return new Singleton();
               }
               else{
                    return singleton;
               }
          }
      } 
      
          //餓漢模式
           public Singleton{
           //類加載時候就創建對象(因爲是靜態變量所以只加載一次保證了實例唯一)
           private static Singleton  singleton = new Singleton()l;
           private Singleton(){ }
           public static Singleton getInstance(){				     
                   return singleton;				 
         }
      } 
      

      以上兩種模式創建單例對象。

    • 有個問題出現瞭如果在多線程情況下會如何?

      我們發現在懶漢模式下可能會出現多個線程創建多個實例的情況,這就違背了我們單例原則了。

    • 如何解決多線程下的安全問題?

      使用synchronized 關鍵字同步代碼塊

         //懶漢模式 
        public Singleton{
             //保存實例化的對象
             private static Singleton  singleton = null;
             private Singleton(){ }
             //加入同步關鍵字使得同一時間只能有一個線程實例化該類
             public static synchronized Singleton getInstance(){
                if(singleton == null){
                     return new Singleton();
                }
                else{
                     return singleton;
                }
           }
       }    
      

      但是我們只有第一次執行纔會同步,顯然上面的每次執行都同步影響效率

    • 我們如何改善多線程?

      使用雙重鎖 首先檢查實例是否創建,如果未創建纔會同步,並且只有第一次同步。

          //懶漢模式 
        public Singleton{
             //使用volatile關鍵字 保證當Single 創建時,多線程能正確處理
             private volatile static Singleton  singleton = null;
             private Singleton(){ }
             //加入同步關鍵字使得同一時間只能有一個線程實例化該類
             public static Singleton getInstance(){
                if(singleton == null){
                      synchronized (Singleton.class){
                           if(singleton == null){
                               singleton = new Singleton();
                           }
                      }			              
                }
                return singleton;
           }
       }  
      
    • 總結

      看到這裏是不是在想單例也不是那麼容易創建的,需要考慮如何保證唯一實例,多線程下如何保證實例唯一,如何優化多線程等等,但是隻要我們一步一步的學習,不要操之過急,認真思考,多加練習,再難的設計模式也能學會後續我會加入工廠模式繼續優化單例的創建

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章