Sunny公司開發人員使用單例模式實現了負載均衡器的設計,可是在實際使用中出現了一個很是嚴重的問題,當負載均衡器在啓動過程當中用戶再次啓動該負載均衡器時,系統無任何異常,但當客戶端提交請求時出現請求分發失敗,經過仔細分析發現原來系統中仍是存在多個負載均衡器對象,致使分發時目標服務器不一致,從而產生衝突。爲何會這樣呢?Sunny公司開發人員百思不得其解。html
如今咱們對負載均衡器的實現代碼進行再次分析,當第一次調用getLoadBalancer()方法建立並啓動負載均衡器時,instance對象爲null值,所以系統將執行代碼instance= new LoadBalancer(),在此過程當中,因爲要對LoadBalancer進行大量初始化工做,須要一段時間來建立LoadBalancer對象。而在此時,若是再一次調用getLoadBalancer()方法(一般發生在多線程環境中),因爲instance還沒有建立成功,仍爲null值,判斷條件(instance== null)爲真值,所以代碼instance= new LoadBalancer()將再次執行,致使最終建立了多個instance對象,這違背了單例模式的初衷,也致使系統運行發生錯誤。java
如何解決該問題?咱們至少有兩種解決方案,在正式介紹這兩種解決方案以前,先介紹一下單例類的兩種不一樣實現方式,餓漢式單例類和懶漢式單例類。編程
1.餓漢式單例類安全
餓漢式單例類是實現起來最簡單的單例類,餓漢式單例類結構圖如圖3-4所示:服務器
2.懶漢式單例類與線程鎖定多線程
除了餓漢式單例,還有一種經典的懶漢式單例,也就是前面的負載均衡器LoadBalancer類的實現方式。懶漢式單例類結構圖如圖3-5所示:併發
假如在某一瞬間線程A和線程B都在調用getInstance()方法,此時instance對象爲null值,均能經過instance == null的判斷。因爲實現了synchronized加鎖機制,線程A進入synchronized鎖定的代碼中執行實例建立代碼,線程B處於排隊等待狀態,必須等待線程A執行完畢後才能夠進入synchronized鎖定代碼。但當A執行完畢時,線程B並不知道實例已經建立,將繼續建立新的實例,致使產生多個單例對象,違背單例模式的設計思想,所以須要進行進一步改進,在synchronized中再進行一次(instance == null)判斷,這種方式稱爲雙重檢查鎖定(Double-Check Locking)。使用雙重檢查鎖定實現的懶漢式單例類完整代碼以下所示:負載均衡
須要注意的是,若是使用雙重檢查鎖定來實現懶漢式單例類,須要在靜態成員變量instance以前增長修飾符volatile,被volatile修飾的成員變量能夠確保多個線程都可以正確處理,且該代碼只能在JDK 1.5及以上版本中才能正確執行。因爲volatile關鍵字會屏蔽Java虛擬機所作的一些代碼優化,可能會致使系統運行效率下降,所以即便使用雙重檢查鎖定來實現單例模式也不是一種完美的實現方式。 函數
|
3.餓漢式單例類與懶漢式單例類比較
餓漢式單例類在類被加載時就將本身實例化,它的優勢在於無須考慮多線程訪問問題,能夠確保實例的惟一性;從調用速度和反應時間角度來說,因爲單例對象一開始就得以建立,所以要優於懶漢式單例。可是不管系統在運行時是否須要使用該單例對象,因爲在類加載時該對象就須要建立,所以從資源利用效率角度來說,餓漢式單例不及懶漢式單例,並且在系統加載時因爲須要建立餓漢式單例對象,加載時間可能會比較長。
懶漢式單例類在第一次使用時建立,無須一直佔用系統資源,實現了延遲加載,可是必須處理好多個線程同時訪問的問題,特別是當單例類做爲資源控制器,在實例化時必然涉及資源初始化,而資源初始化頗有可能耗費大量時間,這意味着出現多線程同時首次引用此類的機率變得較大,須要經過雙重檢查鎖定等機制進行控制,這將致使系統性能受到必定影響。
【做者:劉偉 http://blog.csdn.net/lovelion】
public class Singleton //: IDisposable { private Singleton() { } private static Singleton _Singleton = null; private static object Singleton_Lock = new object(); public static Singleton CreateInstance() { if (_Singleton == null)//保證初始化以後,再也不等待鎖 { lock (Singleton_Lock)//保證單線程進入 { Console.WriteLine("進入鎖"); if (_Singleton == null)//保證只初始化一次 { _Singleton = new Singleton(); } } } return _Singleton; } }