天天看點

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;
 }
}
           

雙if加lock保證多線程的唯一性。

第三種 餓漢模式

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,今天的分享到這了,如果有疑問的可以留言,講的不對的歡迎指出!!!

繼續閱讀