設計模式之 單例模式

一、定義

       單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。

二、基本結構

    一個私有Singleton屬性

private static  Singleton instance = null; 

    一個構造方法

private Singleton(){}  // 確保外部不可見且自身調用

    一個用來外部調用的公有方法,並在此方法中進行創建對象

public static Singleton getInstance(){
      if(instance == null){    // 確保這個類只有一個實例
          instance = new Singleton();
      }
      return instance;
}

    

三、時序圖

    

四、代碼分析

     1、懶漢式,線程不安全

        描述: 這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程。因爲沒有加鎖 synchronized,所以嚴格意義上它並不算單例模式。

public Singleton {
    private static instance = null;
    private Singleton() {   
    }
    public static Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }  
        return instance;  
    }
}

    2、懶漢式,線程安全

      描述:這種方式能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。

        優點:第一次調用才初始化,避免內存浪費。

        缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。

public Singleton {
    private static instance = null;
    private Singleton() {   
    }
    public static Singleton synchronized getInstance() {
        if(instance == null){
            instance = new Singleton();
        }  
        return instance;  
    }
}

    3、餓漢式

        描述:這種方式比較常用,但容易產生垃圾對象。它基於 classloder 機制避免了多線程的同步問題,不過instance 在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法, 但是也不能確定有其他的方式導致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。

        優點:沒有加鎖,執行效率會提高。

        缺點:類加載時就初始化,浪費內存。

public Singleton {
    private static instance = new Singleton();
    private Singleton() {   
    }
    public static Singleton getInstance() {
        return new Singleton();
    }
}

     4、雙檢鎖/雙重校驗鎖    

    這種方式採用雙鎖機制,安全且在多線程情況下能保持高性能。getInstance() 的性能對應用程序很關鍵。

public Singleton {
    private volatile static instance = null;  // volatile 保證對象的原子性
    private Singleton() {   
    }
    public static Singleton getInstance() {
         if(instance == null){ 
            synchronized(Signleton.class) {   
               if(instance == null){
                     instance = new Singleton();
               }
            } 
        }
        return instance;      
    }
}

    5、靜態內部類

        這種方式能達到雙檢鎖方式一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用於靜態域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
這種方式同樣利用了 classloder 機制來保證初始化 instance 時只有一個線程,它跟第 3 種方式不同的是:第 3 種方式只要 Singleton 類被裝載了,那麼 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不一定被初始化。因爲 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,如果實例化 instance 很消耗資源,所以想讓它延遲加載,另外一方面,又不希望在 Singleton 類加載時就實例化,因爲不能確保 Singleton 類還可能在其他的地方被主動使用從而被加載,那麼這個時候實例化 instance 顯然是不合適的。這個時候,這種方式相比第 3 種方式就顯得很合理。

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

五、優缺點

     優點:

        1、提供了對唯一實例的受控訪問。因爲單例類封裝了它的唯一實例,所以它可以嚴格控制客戶怎樣以及何時訪問它,併爲設及開發團隊提供了共享的概念。

        2、由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷燬的對象,單例模式無疑可以提高       系統的性能。    

        3、允許可變數目的實例。我們可以基於單例模式進行擴展,使用與單例控制相似的方法來獲得指定個數的對象實例。

     缺點:    

      1、由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。  

      2、單例類的職責過重,在一定程度上違背了“單一職責原則”。因爲單例類既充當了工廠角色,提供了工廠方法,同時又充當了產品角色,包含一些業務方法,將產品的創建和產品的本身的功能融合到一起

      3、濫用單例將帶來一些負面問題,如爲了節省資源將數據庫連接池對象設計爲單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;現在很多面嚮對象語言(如Java、C#)的運行環境都提供了自動垃圾回收的技術,因此,如果實例化的對象長時間不被利用,系統會認爲它是垃圾,會自動銷燬並回收資源,下次利用時又將重新實例化,這將導致對象狀態的丟失。

六、應用
       1、系統只需要一個實例對象,如系統要求提供一個唯一的序列號生成器,或者需要考慮資源消耗太大而只允許創建一個對象。
       2、客戶調用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。
       3、在一個系統中要求一個類只有一個實例時才應當使用單例模式。反過來,如果一個類可以有幾個實例共存,就需要對單例模式進行改進,使之成爲多例模式、
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章