## 前言: 單例模式是創建型模式5種中的第1種,**關註對象的創建, 保證一個類僅有一個實例,並且提供一個全局訪問點**。在軟體系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。如何繞過常規的構造器,提供一種機制來保證一個類只創建一個實例 ...
前言:
單例模式是創建型模式5種中的第1種,關註對象的創建, 保證一個類僅有一個實例,並且提供一個全局訪問點。在軟體系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。如何繞過常規的構造器,提供一種機制來保證一個類只創建一個實例?
一、應用場景:
- 要求生產唯一序列號。
- WEB 中的計數器,比如不用每次刷新都在資料庫裡加一次,用單例先緩存起來。
- 創建的多個對象需要消耗的資源過多,比如 I/O 與資料庫的連接等。
二、創建與實現:
以下提供兩種情景實現方式,分別是單線程訪問和多線程訪問:
單線程實現:
定義:
public class SingleObject
{
#region 第一種寫法
/// <summary>
/// 創建 SingleObject 的一個對象
/// </summary>
private static SingleObject instance = new SingleObject();
/// <summary>
/// 讓構造函數為 private,這樣該類就不會被實例化
/// </summary>
private SingleObject() { }
/// <summary>
/// 獲取全局唯一對象
/// </summary>
/// <returns></returns>
public static SingleObject getInstance()
{
return instance;
}
#endregion
#region 另外一種寫法
/// <summary>
/// 創建 SingleObject 的一個空對象
/// </summary>
private static SingleObject instance2 = null;
/// <summary>
/// 獲取全局唯一對象
/// </summary>
public static SingleObject getInstance2
{
get
{
if (instance == null)
{
instance = new SingleObject();
}
return instance2;
}
}
#endregion
/// <summary>
/// 單例中定義的方法
/// </summary>
/// <returns></returns>
public string showMessage()
{
return "這是單例設計模式返回的信息....";
}
}
全局單例訪問:
//不合法的構造函數
//編譯時錯誤:構造函數 SingleObject() 是不可見的
//SingleObject object = new SingleObject();
/// <summary>
/// 獲取唯一可用的對象
/// </summary>
SingleObject object = SingleObject.getInstance();
/// <summary>
/// 調用返回消息
/// </summary>
string str = object.showMessage();
說明:以上代碼在單線程情況下不會出現任何問題。但是在多線程的情況下卻不是安全的。如兩個線程同時運行到:
if (instance == null)
判斷是否被實例化,一個線程判斷為True後,在進行創建
instance = new Singleton();
之前,另一個線程也判斷(instance == null),結果也為True。這樣就就違背了Singleton模式的原則,保證一個類僅有一個實例。
多線程實現:
定義:
class Singleton
{
/// <summary>
/// 請註意一個關鍵字volatile,如果去掉這個關鍵字,還是有可能發生線程不是安全的。
/// </summary>
private static volatile Singleton instance = null;
/// <summary>
///創建一個靜態鎖
/// </summary>
private static object lockHelper = new object();
private Singleton() { }
public static Singleton getSingleton
{
get
{
if (instance == null)
{
lock (lockHelper)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
說明:這種方式採用雙鎖機制,安全且在多線程情況下能保持高性能。雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)。此程式對多線程是安全的,使用了一個輔助對象lockHelper,保證只有一個線程創建實例,如果instance為空,保證只有一個線程創建唯一的一個實例。
instance = new Singleton();
(Double Check)請註意一個關鍵字volatile,如果去掉這個關鍵字,還是有可能發生線程不是安全的。volatile 保證嚴格意義的多線程編譯器在代碼編譯時對指令不進行微調。
三、優缺點分析:
優點:
- 在記憶體里只有一個實例,減少了記憶體的開銷,尤其是頻繁的創建和銷毀實例(比如管理首頁頁面緩存)。
- 避免對資源的多重占用(比如寫文件操作)。
缺點:
- 沒有介面,既不能繼承,又與單一職責原則衝突。一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。
本文來自博客園,作者:碼農阿亮,轉載請註明原文鏈接:https://www.cnblogs.com/wml-it/p/17659188.html
技術的發展日新月異,隨著時間推移,無法保證本博客所有內容的正確性。如有誤導,請大家見諒,歡迎評論區指正!
開源庫地址,歡迎點亮:
GitHub:https://github.com/ITMingliang
Gitee: https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang
建群聲明: 本著技術在於分享,方便大家交流學習的初心,特此建立【編程內功修煉交流群】,為大家答疑解惑。熱烈歡迎各位愛交流學習的程式員進群,也希望進群的大佬能不吝分享自己遇到的技術問題和學習心得!進群方式:掃碼關註公眾號,後臺回覆【進群】。