c#設(shè)計模式之單例模式的實現(xiàn)方式
場景描述
單例模式對于我們來說一點也不模式,是一個常見的名稱,單例模式在程序中的實際效果就是:確保一個程序中只有一個實例,并提供一個全局訪問點,節(jié)省系統(tǒng)資源
單例模式無論是在實際開發(fā)中還是在軟件應(yīng)用中比較常見,比如,windows系統(tǒng)的任務(wù)管理器、IIS的HttpApplication、實際項目中的日志組件等等
實現(xiàn)方式
單例模式為了實現(xiàn)一個實例,那么只有不把實例創(chuàng)建暴露出去,只通過類本身來創(chuàng)建實例,為了實現(xiàn)效果,需要定義一個私有構(gòu)造函數(shù)
單例模式實現(xiàn)方式有:餓漢式、懶漢式、雙重驗證式、靜態(tài)內(nèi)部類
下面分別對每一種實現(xiàn)方式做一個簡單的實例,以及其優(yōu)缺點
餓漢式
/// <summary>
/// 創(chuàng)建一個 Singleton 類(餓漢式)
/// 這種方式比較常用,但容易產(chǎn)生垃圾對象。
///優(yōu)點:沒有加鎖,執(zhí)行效率會提高。
///缺點:類加載時就初始化,浪費內(nèi)存。
///它基于 classloder 機制避免了多線程的同步問題,不過,instance 在類裝載時就實例化,
///雖然導(dǎo)致類裝載的原因有很多種,在單例模式中大多數(shù)都是調(diào)用 getInstance 方法,
///但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。
/// </summary>
public class SingleObject
{
//創(chuàng)建 SingleObject 的一個對象
private static SingleObject instance = new SingleObject();
//讓構(gòu)造函數(shù)為 private,這樣該類就不會被實例化
private SingleObject() {
Console.WriteLine("我被創(chuàng)建了.餓漢式");
}
//獲取唯一可用的對象
public static SingleObject GetInstance()
{
return instance;
}
public void ShowMessage()
{
Console.WriteLine("Hello World.餓漢式");
}
}
懶漢式
/// <summary>
/// 創(chuàng)建一個 Singleton 類(懶漢式)
/// 這種方式具備很好的 lazy loading,能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。
/// 優(yōu)點:第一次調(diào)用才初始化,避免內(nèi)存浪費。
/// 缺點:懶漢式在單個線程中沒有問題,但多個線程同事訪問的時候就可能同事創(chuàng)建多個實例,而且這多個實例不是同一個對象。
/// </summary>
public class SingleObject1
{
//創(chuàng)建 SingleObject 的一個對象
private static SingleObject1 instance;
//讓構(gòu)造函數(shù)為 private,這樣該類就不會被實例化
private SingleObject1() { }
//獲取唯一可用的對象
public static SingleObject1 GetInstance()
{
if (instance == null)
{
instance = new SingleObject1();
Console.WriteLine("我被創(chuàng)建了.懶漢式");
}
return instance;
}
public void ShowMessage()
{
Console.WriteLine("Hello World.懶漢式");
}
}
雙重驗證式
/// <summary> /// 創(chuàng)建一個 Singleton 類(雙重驗證) /// 這種方式具備很好的 lazy loading,能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。 /// 優(yōu)點:第一次調(diào)用才初始化,避免內(nèi)存浪費,線程安全。 /// 缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。 /// </summary> public class SingleObject2 { //創(chuàng)建 SingleObject 的一個對象 private static SingleObject2 instance; // 定義一個標(biāo)識確保線程同步 private static readonly object locker = new object(); //讓構(gòu)造函數(shù)為 private,這樣該類就不會被實例化 private SingleObject2() { } //獲取唯一可用的對象 public static SingleObject2 GetInstance() { //// 如果為空,那么就加鎖,創(chuàng)建實例 if (instance == null) { lock (locker) { //// 枷鎖成功后,在做一次非空判斷,避免在加鎖期間以創(chuàng)建了實例而導(dǎo)致重復(fù)創(chuàng)建 if (instance == null) { instance = new SingleObject2(); Console.WriteLine("我被創(chuàng)建了.雙重驗證"); } } } return instance; } public void ShowMessage() { Console.WriteLine("Hello World.雙重驗證"); } }
靜態(tài)內(nèi)部類
/// <summary>
/// 創(chuàng)建一個 Singleton 類(靜態(tài)內(nèi)部類)
/// 這種方式不用加鎖,在效率上和內(nèi)存使用上都比較優(yōu)秀
/// 克服了餓漢模式的不足餓漢模式執(zhí)行效率高,由于在類加載的時候初始化導(dǎo)致內(nèi)存浪費
/// </summary>
public class SingletonStatic
{
/// <summary>
/// 內(nèi)部類
/// </summary>
public class SingletonStaticInner
{
/// <summary>
/// 當(dāng)一個類有靜態(tài)構(gòu)造函數(shù)時,它的靜態(tài)成員變量不會被beforefieldinit修飾
/// 就會確保在被引用的時候才會實例化,而不是程序啟動的時候?qū)嵗?
/// </summary>
static SingletonStaticInner() { }
/// <summary>
/// 實例化
/// </summary>
internal static SingletonStatic singletonStatic = new SingletonStatic();
}
/// <summary>
/// 私有構(gòu)造函數(shù)
/// </summary>
private SingletonStatic() {
Console.WriteLine("我被創(chuàng)建了.靜態(tài)內(nèi)部類");
}
/// <summary>
/// 獲取實例
/// </summary>
/// <returns></returns>
public static SingletonStatic GetInstance()
{
return SingletonStaticInner.singletonStatic;
}
public void ShowMessage()
{
Console.WriteLine("Hello World.靜態(tài)內(nèi)部類");
}
}
每一種創(chuàng)建方式測試
創(chuàng)建一個控制臺程序,通過多線程對每一種實現(xiàn)方式使用,查看其實例次數(shù)分析:
/*
介紹
意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
主要解決:一個全局使用的類頻繁地創(chuàng)建與銷毀。
何時使用:當(dāng)您想控制實例數(shù)目,節(jié)省系統(tǒng)資源的時候。
如何解決:判斷系統(tǒng)是否已經(jīng)有這個單例,如果有則返回,如果沒有則創(chuàng)建。
關(guān)鍵代碼:構(gòu)造函數(shù)是私有的。
應(yīng)用實例:
典型的已有應(yīng)用:
1、windows的任務(wù)管理器等
2、IIS的HttpApplication,所有的HttpModule都共享一個HttpApplication實例
在項目中的實際使用場景:
1、日志組件
2、多線程線程池管理
3、網(wǎng)站計數(shù)器
4、配置文件管理
*/
class Program
{
static void Main(string[] args)
{
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
//// 測試--餓漢式
for (int i = 0; i < 5; i++)
{
taskList.Add(taskFactory.StartNew(() =>
{
SingleObject.GetInstance();
}));
}
//// 測試--懶漢式
for (int i = 0; i < 5; i++)
{
taskList.Add(taskFactory.StartNew(() =>
{
SingleObject1.GetInstance();
}));
}
//// 測試--雙重驗證
for (int i = 0; i < 5; i++)
{
taskList.Add(taskFactory.StartNew(() =>
{
SingleObject2.GetInstance();
}));
}
//// 測試--靜態(tài)內(nèi)部類
for (int i = 0; i < 5; i++)
{
taskList.Add(taskFactory.StartNew(() =>
{
SingletonStatic.GetInstance();
}));
}
Console.ReadLine();
}
}
運行結(jié)果:
通過結(jié)果可以看出:懶漢式實際創(chuàng)建了2個實例,所以在多線程中,懶漢式有線程不安全問題
總結(jié)
根據(jù)單例模式是每一種實現(xiàn)方式對比分析,在實際使用過程中:
如果是單線程應(yīng)用環(huán)境,建議可以采用懶漢模
如果是多線程應(yīng)用環(huán)境,建議采用靜態(tài)內(nèi)部類方式
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對我們的支持。
欄 目:C#教程
本文標(biāo)題:c#設(shè)計模式之單例模式的實現(xiàn)方式
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/4618.html
您可能感興趣的文章
- 01-10C#使用Dispose模式實現(xiàn)手動對資源的釋放
- 01-10深入淺出23種設(shè)計模式
- 01-10Python設(shè)計模式編程中的備忘錄模式與對象池模式示例
- 01-10詳解C#的設(shè)計模式編程之抽象工廠模式的應(yīng)用
- 01-10解析C#設(shè)計模式編程中的裝飾者模式
- 01-10簡單了解C#設(shè)計模式編程中的橋接模式
- 01-10C#編程中使用設(shè)計模式中的原型模式的實例講解
- 01-10剖析設(shè)計模式編程中C#對于組合模式的運用
- 01-10實例解析C#設(shè)計模式編程中簡單工廠模式的使用
- 01-10解析C#設(shè)計模式編程中適配器模式的實現(xiàn)


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機閱讀
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文


