­

雙重檢查鎖定與單例

  • 2020 年 2 月 10 日
  • 筆記

對於單例模式,相信大多數人都可以寫出好幾種實現方法,懶漢,餓漢等等,然而小小單例真要寫好,寫的完全正確也並非易事。

雙重檢查鎖的單例

下面是我們經常使用的一種單例的實現,也就是雙重檢查所的實現方案。

public class Singleton {      private static Singleton instance;        private Singleton() {        }        public Singleton getInstance() {          if (null == instance) {              synchronized (Singleton.class) {                  if (null == instance) {                      instance = new Singleton();   // error                  }              }          }          return uniqueSingleton;      }  }

讓我們來看一下這個程式碼是如何工作的:首先當一個執行緒發出請求後,會先檢查instance是否為null,如果不是則直接返回其內容,這樣避免了進入synchronized塊所需要花費的資源。其次,如果兩個執行緒同時進入了第一個if判斷,那麼他們也必須按照順序執行 synchronized 塊中的程式碼,第一個進入程式碼塊的執行緒會創建一個新的 Singleton 實例,而後續的執行緒則因為無法通過if判斷,而不會創建多餘的實例。

但還有一個問題,在有些情況下,通過這種方式拿到的Singleton對象,可能是錯誤的 。

回顧我們new對象的3個步驟

  • 1,分配記憶體空間
  • 2,初始化對象
  • 3,將對象指向剛分配的記憶體空間

但jvm在指令優化時,會出現步驟2和3對調的情況,比如執行緒1在經過倆層為 null 判斷後,進入 new 的動作,在還沒有初始化對象時,就返加了地址值,執行緒2在第一個為 null 判斷時,因為對象已經不為空,那麼就直接返回了對象。然而當執行緒2打算使用Singleton實例,卻發現它沒有被初始化,於是錯誤發生了。

解決方案

對於上面的問題,有兩種解決方案

1,使用 volatile 關鍵詞主要可以保證程式碼的執行順序不受 jvm 重排序影響。

public class Singleton {      private volatile static Singleton instance;        private Singleton() {      }        public Singleton getInstance() {          if (null == instance) {              synchronized (Singleton.class) {                  if (null == instance) {                      instance = new Singleton();   // error                  }              }          }          return instance;      }  }

2,通過內部類實現多執行緒環境中的單例模式。

public class Singleton {        private Singleton() {      }        private static class SingletonContainer {          private static Singleton instance = new Singleton();      }        public static Singleton getInstance() {          return SingletonContainer.instance;      }  }
作 者:haifeiWu 原文鏈接:https://www.hchstudio.cn