餓漢式單例與懶漢式單例的C#實現
本文將介紹如何使用C#語言實現餓漢式單例與懶漢式單例,並分析餓漢式單例與懶漢式單例的優缺點。
1. 餓漢式單例類
餓漢式單例類(Eager Singleton)是實現起來最容易的單例類,餓漢式單例類結構圖如圖1所示。
圖1 餓漢式單例類圖
從圖1中可以看出,由於在定義靜態變數的時候例項化單例類,因此在類載入時單例物件就已建立,程式碼如下:
class EagerSingleton
{
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton() { }
public static EagerSingleton GetInstance()
{
return instance;
}
}
當類被載入時,靜態變數instance 會被初始化,此時類的私有建構函式會被呼叫,單例類的唯一例項將被建立。
2. 懶漢式單例類與雙重檢查鎖定
與餓漢式單例類相同之處是,懶漢式單例類(Lazy Singleton)的建構函式也是私有的。與餓漢式單例類不同的是,懶漢式單例類在第一次被引用時將自己例項化,在懶漢式單例類被載入時不會將自己例項化。懶漢式單例類結構圖如圖2所示。
圖2 懶漢式單例類圖
從圖2可以看出,在懶漢式單例類中,不是在定義靜態變數時例項化單例類,而是在第一次呼叫靜態工廠方法時例項化單例類。
但是懶漢式單例存在一個很嚴重的問題:如果在高併發、多執行緒環境下實現懶漢式單例類,在某一時刻可能會有多個執行緒需要使用單例物件,即會有多個執行緒同時呼叫GetInstance()方法,可能會造成建立多個例項物件,這將違背單例模式的設計意圖。為了防止生成多個單例物件,需要使用C#語言中的lock關鍵字,lock關鍵字鎖定的程式碼片段稱之為臨界區,可以確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不能進入臨界區。如果其他執行緒試圖進入鎖定的程式碼,則將一直等待,直到該物件被釋放為止。修改之後的懶漢式單例類程式碼如下:
class LazySingleton
{
private static LazySingleton instance = null;
//程式執行時建立一個靜態只讀的輔助物件
private static readonly object syncRoot = new object();
private LazySingleton() { }
public static LazySingleton GetInstance()
{
//第一重判斷,先判斷例項是否存在,不存在再加鎖處理
if (instance == null)
{
//加鎖的程式在某一時刻只允許一個執行緒訪問
lock(syncRoot)
{
//第二重判斷
if(instance==null)
{
instance = new LazySingleton(); //建立單例例項
}
}
}
return instance;
}
}
在上面給出的懶漢式單例類實現程式碼中,對靜態工廠方法GetInstance()中建立單例物件的程式碼進行了加鎖,由於在呼叫時無法確定該單例物件是否已建立,因此需要使用輔助物件syncRoot來進行程式碼鎖定。為了不影響程式的效能,此處只鎖定建立單例物件的程式碼,並未鎖定整個方法。如果例項存在則直接返回,如果例項未建立則加鎖後再建立。
為了更好地對單例物件的建立進行控制,此處使用了一種被稱之為雙重檢查鎖定(Double-CheckLocking)的雙重判斷機制。在雙重檢查鎖定中,當例項不存在且同時有兩個執行緒呼叫GetInstance()方法時,它們都可以通過第一重“instance==null”判斷,然後由於lock鎖定機制,只有一個執行緒進入lock中執行建立程式碼,另一個執行緒處於排隊等待狀態,必須等待第一個執行緒執行完畢後才可以進入lock鎖定的程式碼,如果此時不進行第二重“instance==null”判斷,第二個執行緒並不知道例項已經建立,將繼續建立新的例項,還是會產生多個單例物件,違背單例模式的設計思想,因此需要進行雙重檢查。
3. 餓漢式單例類與懶漢式單例類比較
餓漢式單例類在類被載入時就將自己例項化,它的優點在於無須考慮多個執行緒同時訪問的問題,可以確保例項的唯一性;從呼叫速度和反應時間角度來講,由於單例物件一開始就得以建立,因此要優於懶漢式單例。但是無論系統在執行時是否需要使用該單例物件,由於在類載入時該物件就需要建立,因此從資源利用效率角度來講,餓漢式單例不及懶漢式單例,而且在系統載入時由於需要建立餓漢式單例物件,載入時間可能會比較長。
懶漢式單例類在第一次使用時建立,無須一直佔用系統資源,實現了延遲載入,但是必須處理好多個執行緒同時訪問的問題,特別是當單例類作為資源控制器,在例項化時必然涉及資源初始化,而資源初始化很有可能耗費大量時間,這意味著出現多執行緒同時首次引用此類的機率變得較大,需要通過雙重檢查鎖定等機制進行控制,這將導致系統效能受到一定影響。
Java程式設計師可進一步閱讀:確保物件的唯一性——單例模式 (三)與 確保物件的唯一性——單例模式 (四)
【作者:劉偉 http://blog.csdn.net/lovelion】
相關文章
- C#單例—餓漢式和懶漢式C#單例
- scala:分別使用懶漢式和餓漢式實現單例模式單例模式
- 設計模式:單例模式 (關於餓漢式和懶漢式)設計模式單例
- 【java】單例設計模式——餓漢式和懶漢式、Runtime、TimerJava單例設計模式
- java單例模式懶漢和餓漢Java單例模式
- 詳談單例、餓漢、和懶漢模式單例模式
- 五種單例模式實現懶漢+餓漢+雙重檢測鎖實現+靜態內部類改進餓漢式+列舉單例模式
- java學習回顧---懶漢式和餓漢式Java
- 設計模式:單例模式介紹及8種寫法(餓漢式、懶漢式、Double-Check、靜態內部類、列舉)設計模式單例
- java設計模式之單例模式你真的會了嗎?(懶漢式篇)Java設計模式單例
- 懶漢模式和餓漢模式模式
- 別再用懶漢模式了——從JVM的角度看單例模式模式JVM單例
- 單例模式 – 單例登錄檔與 Spring 實現單例剖析單例模式Spring
- C++之this指標、拷貝構造、賦值構造、單列模式(餓漢模式、懶漢模式)C++指標賦值模式
- 單例模式的 Java 實現與思考單例模式Java
- C# 單例模式的實現和效能對比C#單例模式
- DCL懶載入單例模式單例模式
- DCL單例模式中的缺陷及單例模式的其他實現單例模式
- JS中的單例模式及單例模式原型類的實現JS單例模式原型
- Guava的Supplier實現單例Guava單例
- C#單例模式的用途C#單例模式
- PHP實現單例模式PHP單例模式
- golang實現單例模式Golang單例模式
- 用Rust實現單例Rust單例
- golang如何實現單例Golang單例
- Rust實現單例模式Rust單例模式
- 單例模式 c# 1214單例模式C#
- WPF實現Win10漢堡選單Win10
- Python的類及單例實現Python單例
- 單例模式的各種實現單例模式
- 如何使用CSS繪製一個漢堡式選單CSS
- 餓漢模式C++模式C++
- 單例模式實現對比單例模式
- 單例模式c++實現單例模式C++
- Python中實現單例模式Python單例模式
- 不是單例的單例——巧用ClassLoader單例
- 您的單例模式,真的單例嗎?單例模式
- 利用static來實現單例模式單例模式
- 單例模式:5種實現方式單例模式