在上一篇博客中(https://www.cnblogs.com/t140603/p/10318228.html)提到的第二個單例模式的實例為:
public class SingLetonClass
{
//定義一個用於保存靜態變量的實例
private static SingLetonClass instance = null;
//定義一個保證線程同步的標識
private static readonly object locker = new object();
//構造函數為私有,使外界不能創建該類的實例
private SingLetonClass()
{
}
public static SingLetonClass GetInstance()
{
lock (locker)
{
if (instance == null)
{
SingLetonClass instance = new SingLetonClass();
}
}
return instance;
}
}
然而這個實現方式雖然是安全的,但是是不是最好的呢,還能不能再次進行優化?
下面就分析一下多線程中單例模式的運行:
當程序的線程執行到鎖時,就會停下來判斷是否有其他線程在執行鎖里面的代碼,沒有才會進入繼續執行,那么如果多條線程同時運行到鎖的時候就只能一條一條進入鎖,進入並判斷是否已被實例化,如有就跳出,如無,則進行實例化。
上面,我們對多線程中的單例模式進行了分析,從分析中我們可以看出,每條線程都要先進入鎖才做判斷,但在沒進入之前,線程就已經可以判斷出是否有過實例,那為什么我們不在外面就先進行判斷再進入鎖呢?下面就給出優化后的代碼:即再鎖的外面多加一次判斷。
public class SingLetonClass
{
//定義一個用於保存靜態變量的實例
private static SingLetonClass instance = null;
//定義一個保證線程同步的標識
private static readonly object locker = new object();
//構造函數為私有,使外界不能創建該類的實例
private SingLetonClass()
{
}
public static SingLetonClass GetInstance()
{
if (instance == null)
{
lock (locker)
{
if (instance == null)
{
SingLetonClass instance = new SingLetonClass();
}
}
return instance;
}
}
}
從更改后的代碼上來看,用了兩個if進行判斷的原因還是在於多線程的特性,還是因為多線程訪問函數時都是獨立訪問的,所以才要用到兩個if(如果對多線程特性不太了解的童鞋,可自行查資料研究一下)。
先分析內層的if語句塊,在這個語句塊執行之前,先進行了加鎖操作,從而保證只有一個線程能夠訪問該語句塊,也就是保證只創建了一個實例。再分析外層的if語句塊,這使得每個線程欲獲取實例時不必每次都得加鎖,因為只有實例為空時(即需要創建一個實例),才需加鎖創建,若果已存在一個實例,就直接返回該實例,這樣就節省了性能開銷。
這個過程實際上不是雙重鎖,只是進行了兩次判斷,因為真正的鎖機制代價是比較高的,所以本文介紹的機制可以稱為雙檢鎖機制。