1. 程式人生 > 實用技巧 >《設計模式之禪》(2)單例模式

《設計模式之禪》(2)單例模式

單例模式

Singleton Pattern

定義:Ensure a class has only one instance, and provide a global point of access to it.

(確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項)

Singleton類 稱為單例類,通過使用private的建構函式確保了在一個應用中只產生一個例項並且是

自行例項化的(在Singleton中自己使用new Singleton)。

通用的單例模式程式碼:(餓漢式)

public class Singleton{
    private static final Singleton singleton = new Singleton();
    //限制產生多個物件
    public Singleton(){}
    //獲得例項物件
    public static Singleton getSingleton(){
        return singleton;
    }        
    //類中其他的方法,儘量是static
   public static void dosomething(){
    }
}

  

應用:

1.優點:

  (1)減少了記憶體開支,特別是一個物件需要頻繁地建立銷燬的時候,而且建立或者銷燬效能又無法優化。

  (2)減少了系統的開銷,當一個物件產生需要較多的資源的時候,如讀取配置、產生其他依賴物件時候,可以

在啟動的時候直接產生一個單例物件,然後永駐駐留記憶體的方式來解決。

  (3)可以避免多資源的多重佔用,例如一個寫檔案動作,只有一個例項的存在,避免堆同一個資原始檔的同事寫操作。

  (4)可以設定全域性的訪問點,優化和共享資源訪問,例如可以設計一個單例類,負責所有資料表的對映處理。

缺點:

  (1)一般沒有介面,擴充套件很難。

  (2)對於測試是不利的,沒法使用mock的方式虛擬一個物件。

  (3)與單一職責原則有衝突

場景:

  (1)要求生成唯一序列號的環境。

  (2)需要一個共享訪問點或者共享的資料,入一個web頁面上的計數器,可以不用每次重新整理都記錄到資料庫中。

  (3)建立一個物件需要消耗的資源過多,如訪問IO和資料庫等資源。

  (4)需要定義大量的靜態常量和靜態方法的環境

注意事項:

  高併發情況下,執行緒同步問題:

  執行緒不安全的單例:(懶漢式)

  如果一個執行緒A執行到singleton = new Singleton(),沒有獲取到物件(初始化是需要時間的),

第二個執行緒B也執行,執行到(singleton == null)判斷,那麼執行緒B的判斷條件也為真,於是兩個

執行緒同時執行下去,那麼記憶體中就出現了兩個物件。

public class Singleton{
    private static final Singleton singleton = null;
    //限制產生多個物件
    public Singleton(){}
    //獲得例項物件
    public static Singleton getSingleton(){
        if(singleton == null){
             singleton = new Singleton();
         }  
        return singleton;
    }        
    //類中其他的方法,儘量是static
   public static void dosomething(){
    }
}

  

更詳細的介紹和寫法:

https://www.runoob.com/design-pattern/singleton-pattern.html

類載入順序

類載入(classLoader)機制一般遵從下面的載入順序

如果類還沒有被載入:

  • 先執行父類的靜態程式碼塊和靜態變數初始化,靜態程式碼塊和靜態變數的執行順序跟程式碼中出現的順序有關。
  • 執行子類的靜態程式碼塊和靜態變數初始化。
  • 執行父類的例項變數初始化
  • 執行父類的建構函式
  • 執行子類的例項變數初始化
  • 執行子類的建構函式

父類static => 子類static => 父類建構函式 => 子類的例項初始化 => 子類的建構函式

同時,載入類的過程是執行緒私有的,別的執行緒無法進入。

如果類已經被載入:

靜態程式碼塊和靜態變數不在重複執行,再建立類物件時,只執行與例項相關的變數初始化和構造方法。

static關鍵字

一個類中如果有成員變數或者方法被static關鍵字修飾,那麼該成員變數或方法將獨立於該類的任何物件。

它不依賴類特定的例項,被類的所有例項共享,只要這個類被載入,該成員變數或方法就可以通過類名去進行訪問,

它的作用用一句話來描述就是,不用建立物件就可以呼叫方法或者變數,這簡直就是為單例模式的程式碼實現量身打造的。

下面將列舉幾種單例模式的實現方式,其關鍵方法都是用static修飾的,並且,為了避免單例的類被頻繁建立物件,

我們可以用private的建構函式來確保單例類無法被外部例項化。

雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}