C# 單例模式 1、定義:單例模式就是保證在整個應用程式的生命周期中,在任何時刻,被指定的類只有一個實例,併為客戶程式提供一個獲取該實例的全局訪問點。 2、單例模式的優點有: (1)實例控制:單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。 (2)靈活性:因為類控 ...
C# 單例模式
1、定義:單例模式就是保證在整個應用程式的生命周期中,在任何時刻,被指定的類只有一個實例,併為客戶程式提供一個獲取該實例的全局訪問點。
首先來明確一個問題,那就是在某些情況下,有些對象,我們只需要一個就可以了。
2、單例模式的優點有:
(1)實例控制:單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
(2)靈活性:因為類控制了實例化過程,所以類可以靈活更改實例化過程。
3、單例模式的缺點有:
(1)開銷:雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
(2)可能的開發混淆:使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程式開發人員可能會意外發現自己無法直接實例化此類。
4、舉個慄子:
一臺電腦上可以連好幾個印表機,但是這個電腦上的列印程式只能有一個,這裡就可以通過單例模式來避免兩個列印作業同時輸出到印表機中,即在整個的列印過程中我只有一個列印程式的實例。其實生活中也有多類似的例子,比如操作ATM機的時候,存錢和取錢的操作是不能同時做的,只能一個一個來完成;
5.代碼解析
第一種 最簡單,但沒有考慮線程安全,在多線程時可能會出問題.代碼如下
解析如下:
1)首先,該Singleton的構造函數必須是私有的,以保證客戶程式不會通過new()操作產生一個實例,達到實現單例的目的;
2)因為靜態變數的生命周期跟整個應用程式的生命周期是一樣的,所以可以定義一個私有的靜態全局變數instance來保存該類的唯一實例;
3)必須提供一個全局函數訪問獲得該實例,並且在該函數提供控制實例數量的功能,即通過if語句判斷instance是否已被實例化,如果沒有則可以同new()創建一個實例;否則,直接向客戶返回一個實例。
在這種經典模式下,沒有考慮線程併發獲取實例問題,即可能出現兩個線程同時獲取instance實例,且此時其為null時,就會出現兩個線程分別創建了instance,違反了單例規則。因此,下麵代碼修改。
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton GetInstance() { if(instance==null) { instance=new Singleton(); } return instance; } }
第二種 為多線程下的單例模式,考慮了線程安全,代碼如下:
解析如下:
使用了雙重鎖方式較好地解決了多線程下的單例模式實現。先看內層的if語句塊,使用這個語句塊時,先進行加鎖操作,保證只有一個線程可以訪問該語句塊,進而保證只創建了一個實例。
再看外層的if語句塊,這使得每個線程欲獲取實例時不必每次都得加鎖,因為只有實例為空時(即需要創建一個實例),才需加鎖創建,若果已存在一個實例,就直接返回該實例,節省了性能開銷。
public class Singleton { private static Singleton instance; private static object _lock=new object(); private Singleton() { } public static Singleton GetInstance() { if(instance==null) { lock(_lock) { if(instance==null) { instance=new Singleton(); } } } return instance; } }
第三種 餓漢模式
Eager Singleton(餓漢式單例類),其靜態成員在類載入時就被初始化,此時類的私有構造函數被調用,單例類的唯一實例就被創建
這種模式的特點是自己主動實例,代碼如下
使用的readonly關鍵可以跟static一起使用,用於指定該常量是類別級的,它的初始化交由靜態構造函數實現,並可以在運行時編譯。在這種模式下,無需自己解決線程安全性問題,CLR會給我們解決。由此可以看到這個類被載入時,會自動實例化這個類,而不用在第一次調用GetInstance()後才實例化出唯一的單例對象。
public sealed class Singleton { private static readonly Singleton instance=new Singleton(); private Singleton() { } public static Singleton GetInstance() { return instance; } }
第四種 懶漢模式
Lazy Singleton(懶漢式單例類),其類的唯一實例在真正調用時才被創建,而不是類載入時就被創建。所以Lazy Singleton不是線程安全的。
public class Singleton
{ private static Singleton singleton = null; public static synchronized Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; }
}
總結:
單例中懶漢和餓漢的本質區別在於以下幾點:
1、餓漢式是線程安全的,在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不在改變。懶漢式如果在創建實例對象時不加上synchronized則會導致對對象的訪問不是線程安全的。
2、從實現方式來講他們最大的區別就是懶漢式是延時載入,他是在需要的時候才創建對象,而餓漢式在虛擬機啟動的時候就會創建,餓漢式無需關註多線程問題,寫法簡單明瞭,能用則用。但是它是載入類時創建實例。所以如果是一個工廠模式,緩存了很多實例,那麼就得考慮效率問題,因為這個類一載入則把所有實例不管用不用一塊創建。
3、兩者建立單例對象的時間不同。“懶漢式”是在你真正用到的時候才去建這個單例對象,“餓漢式”是在不管用不用得上,一開始就建立這個單例對象。
ok,今天的分享到這了,如果有疑問的可以留言,講的不對的歡迎指出!!!