<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 確保對象的唯一性——單例模式 (三) 3.4 餓漢式單例與懶漢式單例的討論 Sunny公司開發人員使用單例模式實現了負載均衡器的設計,但是在實際使用中出現了一個非常嚴重的問題,當負載均衡器在啟動過程中用戶再次啟動該負載均衡器時,系統無任何異常,但當客戶端提交請求時出現請求分發失敗,通過仔細分析發現原來系統中還是存在多個負載均衡器對象,導致分發時目標服務器不一致,從而產生沖突。為什么會這樣呢?Sunny公司開發人員百思不得其解。 現在我們對負載均衡器的實現代碼進行再次分析,當第一次調用getLoadBalancer()方法創建并啟動負載均衡器時,instance對象為null值,因此系統將執行代碼instance= new LoadBalancer(),在此過程中,由于要對LoadBalancer進行大量初始化工作,需要一段時間來創建LoadBalancer對象。而在此時,如果再一次調用getLoadBalancer()方法(通常發生在多線程環境中),由于instance尚未創建成功,仍為null值,判斷條件(instance== null)為真值,因此代碼instance= new LoadBalancer()將再次執行,導致最終創建了多個instance對象,這違背了單例模式的初衷,也導致系統運行發生錯誤。 如何解決該問題?我們至少有兩種解決方案,在正式介紹這兩種解決方案之前,先介紹一下單例類的兩種不同實現方式,餓漢式單例類和懶漢式單例類。 1.餓漢式單例類 餓漢式單例類是實現起來最簡單的單例類,餓漢式單例類結構圖如圖3-4所示: ![](http://my.csdn.net/uploads/201204/02/1333305889_1823.gif) 從圖3-4中可以看出,由于在定義靜態變量的時候實例化單例類,因此在類加載的時候就已經創建了單例對象,代碼如下所示: ``` class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton getInstance() { return instance; } } ``` 當類被加載時,靜態變量instance會被初始化,此時類的私有構造函數會被調用,單例類的唯一實例將被創建。如果使用餓漢式單例來實現負載均衡器LoadBalancer類的設計,則不會出現創建多個單例對象的情況,可確保單例對象的唯一性。 2.懶漢式單例類與線程鎖定 除了餓漢式單例,還有一種經典的懶漢式單例,也就是前面的負載均衡器LoadBalancer類的實現方式。懶漢式單例類結構圖如圖3-5所示: ![](http://my.csdn.net/uploads/201204/02/1333305983_8045.gif) 從圖3-5中可以看出,懶漢式單例在第一次調用getInstance()方法時實例化,在類加載時并不自行實例化,這種技術又稱為延遲加載(Lazy Load)技術,即需要的時候再加載實例,為了避免多個線程同時調用getInstance()方法,我們可以使用關鍵字synchronized,代碼如下所示: ``` class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } synchronized public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } } ``` 該懶漢式單例類在getInstance()方法前面增加了關鍵字synchronized進行線程鎖,以處理多個線程同時訪問的問題。但是,上述代碼雖然解決了線程安全問題,但是每次調用getInstance()時都需要進行線程鎖定判斷,在多線程高并發訪問環境中,將會導致系統性能大大降低。如何既解決線程安全問題又不影響系統性能呢?我們繼續對懶漢式單例進行改進。事實上,我們無須對整個getInstance()方法進行鎖定,只需對其中的代碼“instance = new LazySingleton();”進行鎖定即可。因此getInstance()方法可以進行如下改進: ``` public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { instance = new LazySingleton(); } } return instance; } ``` 問題貌似得以解決,事實并非如此。如果使用以上代碼來實現單例,還是會存在單例對象不唯一。原因如下: 假如在某一瞬間線程A和線程B都在調用getInstance()方法,此時instance對象為null值,均能通過instance == null的判斷。由于實現了synchronized加鎖機制,線程A進入synchronized鎖定的代碼中執行實例創建代碼,線程B處于排隊等待狀態,必須等待線程A執行完畢后才可以進入synchronized鎖定代碼。但當A執行完畢時,線程B并不知道實例已經創建,將繼續創建新的實例,導致產生多個單例對象,違背單例模式的設計思想,因此需要進行進一步改進,在synchronized中再進行一次(instance == null)判斷,這種方式稱為雙重檢查鎖定(Double-Check Locking)。使用雙重檢查鎖定實現的懶漢式單例類完整代碼如下所示: ``` class LazySingleton { private volatile static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { //第一重判斷 if (instance == null) { //鎖定代碼塊 synchronized (LazySingleton.class) { //第二重判斷 if (instance == null) { instance = new LazySingleton(); //創建單例實例 } } } return instance; } } ``` 需要注意的是,如果使用雙重檢查鎖定來實現懶漢式單例類,需要在靜態成員變量instance之前增加修飾符volatile,被volatile修飾的成員變量可以確保多個線程都能夠正確處理,且該代碼只能在JDK 1.5及以上版本中才能正確執行。由于volatile關鍵字會屏蔽Java虛擬機所做的一些代碼優化,可能會導致系統運行效率降低,因此即使使用雙重檢查鎖定來實現單例模式也不是一種完美的實現方式。 擴展 IBM公司高級軟件工程師Peter Haggar 2004年在IBM developerWorks上發表了一篇名為《雙重檢查鎖定及單例模式——全面理解這一失效的編程習語》的文章,對JDK 1.5之前的雙重檢查鎖定及單例模式進行了全面分析和闡述,參考鏈接:http://www.ibm.com/developerworks/cn/java/j-dcl.html 3.餓漢式單例類與懶漢式單例類比較 餓漢式單例類在類被加載時就將自己實例化,它的優點在于無須考慮多線程訪問問題,可以確保實例的唯一性;從調用速度和反應時間角度來講,由于單例對象一開始就得以創建,因此要優于懶漢式單例。但是無論系統在運行時是否需要使用該單例對象,由于在類加載時該對象就需要創建,因此從資源利用效率角度來講,餓漢式單例不及懶漢式單例,而且在系統加載時由于需要創建餓漢式單例對象,加載時間可能會比較長。 懶漢式單例類在第一次使用時創建,無須一直占用系統資源,實現了延遲加載,但是必須處理好多個線程同時訪問的問題,特別是當單例類作為資源控制器,在實例化時必然涉及資源初始化,而資源初始化很有可能耗費大量時間,這意味著出現多線程同時首次引用此類的機率變得較大,需要通過雙重檢查鎖定等機制進行控制,這將導致系統性能受到一定影響。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看