1. 程式人生 > >單例模式(Singleton Pattern)典型的四種實現方式

單例模式(Singleton Pattern)典型的四種實現方式

一、簡介:

引入百度百科對單例模式的介紹:

單例模式(Singleton Pattern)是一種常用的軟體設計模式。《設計模式》一書中對其介紹:“保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。”
在某些時候,一個系統只有一個例項是很重要且必要的,比如:Windows系統中的工作管理員, 同時只能存在一個,因為系統瞬態是固定的,存在多個工作管理員也是在浪費資源。

優點:

  1. 在記憶體裡只有一個例項,減少了記憶體的開銷,尤其是頻繁的建立和銷燬例項(比如管理學院首頁頁面快取)。
  2. 避免對資源的多重佔用(比如寫檔案操作)。

缺點

  1. 沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來例項化。

二、單例模式的實現(Java):

  • 這裡只介紹典型的四種實現方式,懶漢、餓漢、雙檢鎖、登記式/靜態內部類。至於其他變種也都是在此基礎上變化的,看多了反而凌亂。
  • 實現單例注意3個點:1.單例類只能有一個例項(建構函式私有化)。2.單例類必須自己建立自己的唯一例項。3.暴露給外部一個訪問點。

1.懶漢模式:

  • 多個執行緒可能同時進入if中,最終會建立多個例項,單執行緒情況下可以使用
  • 懶漢模式執行緒不安全,但是有人會想,直接在getInstance()方法加個synchronized
    關鍵字不就好了?確實,這種寫法是能保證多執行緒安全,但是執行緒每次獲取例項時,都需要先拿到鎖,影響效率。
    /**
     * 延遲載入,執行緒不安全
     */
     private static Singleton instance;
     private Singleton(){}
     public static Singleton getInstance(){
         if(instance==null){
             instance = new Singleton();
         }
         return instance;
     }

2.餓漢模式:

  • 基於 ClassLoader 機制避免了多執行緒的同步問題,但是在類載入時就建立了例項,浪費了記憶體。
  • java中的Runtime類就是一個典型的單例實現,它記錄了java程式執行時的環境,且允許應用程式與應用程式正在執行的環境進行互動。
    /**
     * 無延遲載入,執行緒安全
     */
     private static final Singleton instance = new Singleton();
     private Singleton() {}
     public static Singleton getInstance() {
         return instance;
     }

3.雙檢鎖模式:

  • 這種方式採用雙鎖機制,安全且在多執行緒情況下能保持高效能。
  • volatile 關鍵字作用主要是防止 instance = new Singleton() 指令重排,因為這句程式碼不是一個原子操作,而是①new Singleton()堆中分配記憶體②初始化Singleton成員變數③instance指向記憶體中剛開闢出的空間,如果不加volatile關鍵字,那麼執行順序①②③,由於指令重排可能變為①③②,當執行完①③之後,如果被另一個執行緒搶佔了資源,那麼判斷instance==null時,結果為false,然後會直接返回instance,此時未初始化,導致使用時可能會報錯。
  • -
      /**
       * 延遲載入,執行緒安全
       */
       private static volatile Singleton instance;
       private Singleton(){}
       public static Singleton getInstance(){
           //第一次檢測,避免每次呼叫getInstance()時都需要獲取同步鎖
           //提高了效率
           if(instance==null){ 
               //this
               synchronized(Singleton.class){
                   //第二次檢測,保證多執行緒下安全,因為可能多個
                   //執行緒同時進入this位置。
                   if(instance==null){
                       instance = new Singleton();
                   }
               } 
           }
           return instance;
       }

4.登記式/靜態內部類:

  • 這種方式同樣利用了 classloder 機制來保證初始化 instance 時只有一個執行緒,因為 SingletonHolder 類沒有被主動使用,只有通過顯式呼叫 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而例項化 instance。
  /**
   * 延遲載入 多執行緒安全
   */
   private Singleton(){}
   private static class InnerSingleton{
      private  static final Singleton INSTANCE = new Singleton(); 
   }
   public static final Singleton getInstance(){
     return   InnerSingleton.INSTANCE;
   }