單例模式的雙層鎖原理
public class SingleTon { private static SingleTon singleTon = null; public SingleTon() { // TODO Auto-generated constructor stub } public static SingleTon getInstance(){ if (singleTon == null) { synchronized (SingleTon.class) { if (singleTon == null) { singleTon = new SingleTon(); } } } return singleTon; } }
為何要使用雙重檢查鎖定呢?上文已經大概說了一下。
考慮這樣一種情況,就是有兩個執行緒同時到達,即同時呼叫 getInstance() 方法,
此時由於 singleTon == null ,所以很明顯,兩個執行緒都可以通過第一重的 singleTon == null ,
進入第一重 if 語句後,由於存在鎖機制,所以會有一個執行緒進入 lock 語句並進入第二重 singleTon == null ,
而另外的一個執行緒則會在 lock 語句的外面等待。
而當第一個執行緒執行完 new SingleTon()語句後,便會退出鎖定區域,此時,第二個執行緒便可以進入
lock 語句塊,
此時,如果沒有第二重 singleTon == null 的話,那麼第二個執行緒還是可以呼叫 new SingleTon ()語句,
這樣第二個執行緒也會建立一個 SingleTon例項,這樣也還是違背了單例模式的初衷的,
所以這裡必須要使用雙重檢查鎖定。
細心的朋友一定會發現,如果我去掉第一重 singleton == null ,程式還是可以在多執行緒下完好的執行的,
考慮在沒有第一重 singleton == null 的情況下,
當有兩個執行緒同時到達,此時,由於 lock 機制的存在,第一個執行緒會進入 lock 語句塊,並且可以順利執行 new SingleTon(),
當第一個執行緒退出 lock 語句塊時, singleTon 這個靜態變數已不為 null 了,所以當第二個執行緒進入 lock 時,
還是會被第二重 singleton == null 擋在外面,而無法執行 new Singleton(),
所以在沒有第一重 singleton == null 的情況下,也是可以實現單例模式的?那麼為什麼需要第一重 singleton == null 呢?
這裡就涉及一個性能問題了,因為對於單例模式的話,new SingleTon()只需要執行一次就 OK 了,
而如果沒有第一重 singleTon == null 的話,每一次有執行緒進入 getInstance()時,均會執行鎖定操作來實現執行緒同步,
這是非常耗費效能的,而如果我加上第一重 singleTon == null 的話,
那麼就只有在第一次,也就是 singleTton ==null 成立時的情況下執行一次鎖定以實現執行緒同步,
而以後的話,便只要直接返回 Singleton 例項就 OK 了而根本無需再進入 lock 語句塊了,這樣就可以解決由執行緒同步帶來的效能問題了。