單例模式
含義
即 保證一類僅有一個實例,並提供一個訪問它的全局訪問點。
優缺點
- 優點:
- 確保所有對象都訪問一個實例
- 節省內存,避免頻繁地創建、銷毀對象
- 避免對共享資源的多重占用
- 缺點:
- 違背了"單一職責原則"
- 可擴展性差
實現過程
- 保證單一實例:創建private static類型的對象instance,其為空時才new實例化創建
- 全局訪問點:創建public類GetInstance()方法用於全局訪問instance,並擔當檢測、new instance的責任
概念圖
代碼實現
//單例類:
class Singleton
{
//私有化的構造函數
private Singleton() { Console.WriteLine("This is a SingleObject"); }
//單一的實例
private static Singleton instance;
//全局訪問點
public static Singleton GetInstance()
{
//如果實例為空,則new一個新實例
if (instance == null)
{
instance = new Singleton();
Console.WriteLine("Create a new Instance");
}
else
{
Console.WriteLine("Get a Instance");
}
return instance;
}
}
//測試類:
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance2();
Singleton s2 = Singleton.GetInstance2();
/* OUT:
This is a SingleObject
Create a new Instance
Get a Instance
*/
}
}
單例模式分類
根據new關鍵字實例化單例的先后順序,可把單例模式分為餓漢式單例、懶漢式單例:
餓漢式
- 含義:開始時就實例化instance
- 優點:線程安全(因為instance是static類型)
- 缺點:不管是否使用對象,開始時就實例化並占用了空間。即空間換時間
//餓漢式:開始時就實例化instance
public class Singleton
{
private Singleton (){}
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance;
}
}
懶漢式
- 含義:需要時才實例化instance
- 優點:資源利用率高。即時間換空間
- 缺點:多線程下存在隱患
//懶漢式:需要時才實例化instance
public class Singleton
{
private Singleton (){}
private static Singleton instance;
public static Singleton getInstance()
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
多線程中的單例
在上述懶漢式單例中,若多個線程同時進行到if (instance == null)
,會全部檢測通過,最終造成多線程下創建了多個實例,即 多線程不安全。因此需要對多線程下的單例模式進行調整,使用 lock機制實現線程安全:
//餓漢式 + 線程安全 + 單鎖
class Singleton
{
private Singleton() {}
private static Singleton instance;
//靜態只讀對象用於輔助實現lock
private static readonly object locker = new object();
public static Singleton GetInstance()
{
//lock機制:確保一個線程位於該臨界區時,其他線程不可再進入,直至當前線程訪問完畢
lock (locker)
{
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
雖然加了lock鎖實現了懶漢模式下的線程安全,但我們不難發現一個問題:若已經存在instance實例,在執行GetInstance()時還有必要lock{}嗎?顯然不需要,lock的使用必然是消耗一定空間的,因此為了節省lock的空間,采用更優解法:雙重鎖定(Double-Check-Locking):
//餓漢式 + 線程安全 + 雙鎖
class Singleton
{
private Singleton() {}
private static Singleton instance;
//靜態只讀對象用於輔助實現lock
private static readonly object locker = new object();
public static Singleton GetInstance()
{
//首次檢驗:是否需要加鎖
if(instance == null)
{
//為當前線程加鎖
lock (locker)
{
//再次檢驗:避免當前線程實例化instance后,下一個線程再次實例
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
}