普通的單例模式是線程不安全的,驗證方法如下:
```
sealed class Singleton1
? ? {
? ? ? ? private Singleton1()
? ? ? ? {
? ? ? ? }
? ? ? ? private static Singleton1 instance = null;
? ? ? ? public static Singleton1 Instance
? ? ? ? {
? ? ? ? ? ? get
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (instance == null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("Cup");
? ? ? ? ? ? ? ? ? ? instance = new Singleton1();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return instance;
? ? ? ? ? ? }
? ? ? ? }
? ? }
class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? Singleton1 st1 = null;
? ? ? ? ? ? Singleton1 st2 = null;
? ? ? ? ? ? while (true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread t1 = new Thread(() =>
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? st1 = Singleton1.Instance;
? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? Thread t2 = new Thread(() =>
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? st2 = Singleton1.Instance;
? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? t1.Start();
? ? ? ? ? ? ? ? t2.Start();
? ? ? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? }
? ? ? ? }
? ? }
```
如上所示,打印的結(jié)果有很大概率出現(xiàn)兩次"Cup",說明兩個線程都創(chuàng)建了新的對象,單例被打破了。
改進方式:加鎖
```
sealed class Singleton1
? ? {
? ? ? ? private Singleton1()
? ? ? ? {
? ? ? ? }
? ? ? ? private static readonly object syncObj = new object();
? ? ? ? private static Singleton1 instance = null;
? ? ? ? public static Singleton1 Instance
? ? ? ? {
? ? ? ? ? ? get
? ? ? ? ? ? {
? ? ? ? ? ? ? ? lock (syncObj)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (instance == null)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? Console.WriteLine("Cup");
? ? ? ? ? ? ? ? ? ? ? ? instance = new Singleton1();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return instance;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? public void Clear()
? ? ? ? {
? ? ? ? ? ? instance = null;
? ? ? ? }
? ? }
```
運行結(jié)果只有一個"Cup",程序在進入代碼段時首先判斷有沒有加鎖,如果沒有就加鎖,另一個線程判斷代碼已經(jīng)有鎖了,就直接返回,從而保證了單例的唯一性。
缺點:判斷鎖狀態(tài)和嘗試加鎖操作比較消耗性能
改進:鎖前判斷:
```
public static Singleton1 Instance
? ? ? ? {
? ? ? ? ? ? get
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (instance == null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? lock (syncObj)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? if (instance == null)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? Console.WriteLine("Cup");
? ? ? ? ? ? ? ? ? ? ? ? ? ? instance = new Singleton1();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return instance;
? ? ? ? ? ? }
? ? ? ? }
```
如此,就只有第一次試圖創(chuàng)建實例時要加鎖
有沒有不用鎖的辦法呢,也有:
```
sealed class Singleton1
? ? {
? ? ? ? private Singleton1()
? ? ? ? {
? ? ? ? }
? ? ? ? private static Singleton1 instance = new Singleton1();
? ? ? ? public static Singleton1 Instance
? ? ? ? {
? ? ? ? ? ? get
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (instance != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? return instance;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
```
C#在調(diào)用靜態(tài)構(gòu)造函數(shù)時初始化靜態(tài)變量,運行時能夠確保只調(diào)用一次靜態(tài)構(gòu)造函數(shù)。但這種機制不確定在其他語言中也存在
如果局限于C#中,還有更優(yōu)化的方法:
```
sealed class Singleton1
? ? {
? ? ? ? private Singleton1()
? ? ? ? {
? ? ? ? }
? ? ? ? public static Singleton1 Instance
? ? ? ? {
? ? ? ? ? ? get
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return Nested.instance;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? class Nested
? ? ? ? {
? ? ? ? ? ? static Nested()
? ? ? ? ? ? {
? ? ? ? ? ? }
? ? ? ? ? ? internal static readonly Singleton1 instance = new Singleton1();
? ? ? ? }
? ? }
```
將創(chuàng)建實例的時機局限在獲取Instance時。