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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章