單例模式的實現方式

最近看到組里有人實現單例模式,采用靜態內部類的方式,不是很懂這種寫法的優點,查了一下各種寫法的優缺點,總結一下。
內容多處參考文章:http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/

懶漢式

public class Single {

      private static Single mInstance;
      private Single(){}

      // 線程不安全
      public static Single getInstance() {
          if (mInstance == null) {
              mInstance = new Single();
          }
          return mInstance;
      }

      // 線程安全,效率低,只有一個線程能調用getInstance()方法。
      public static synchronized Single getInstance() {
          if (mInstance == null) {
              mInstance = new Single();
          }
          return mInstance;
      }

      // 同步代碼塊加鎖,雙重檢查鎖。
      public static Single getInstance() {
          if (mInstance == null) {  //Single Checked 
              synchronized (Single.class) {
                  if (mInstance == null) { //Double Checked 
                      mInstance = new Single(); 
                  } 
              } 
          } 
          return mInstance ;
      }
}

同步代碼塊加鎖,雙重檢查

  • 這個是平時最常用的方式,看似完美,其實是有問題的。
    因為mInstance = new Single();這句語句的執行,不是一個原子操作,JVM在執行這條語句時,做了3個操作。
  1. 給mInstance分配內存。
  2. 調用Single的構造方法進行初始化。
  3. 將mInstance對象指向分配的內存空間(執行完這步mInstance就非空啦)。

但是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯。

餓漢式

public class Single {
      // 類加載時就被初始化,線程安全
      private static final Single mInstance = new Single();
      private Single(){}

      public static Single getInstance() {
          return mInstance;
      }
}

缺點

  • 不是懶加載模式,類被加載時就被初始化。
  • 如果構造函數需要傳遞參數時,不能滿足。

靜態內部類

public class Single {

      private Single(){}

      private static class InnerHolder {
            private static final INSTANCE = new Single();
      }

      public static Single getInstance() {
            return InnerHolder.INSTANCE;
      }
}

這種寫法仍然使用JVM本身機制保證了線程安全問題;由于 InnerHolder 是私有的,除了 getInstance() 之外沒有辦法訪問它,因此它是懶漢式的;同時讀取實例的時候不會進行同步,沒有性能缺陷;也不依賴 JDK 版本。

總結

單例模式最好采用靜態內部類實現,但是如果對懶加載參數沒有要求,餓漢式也可以。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容