單例模式的實現(xiàn)方式

最近看到組里有人實現(xiàn)單例模式,采用靜態(tài)內(nèi)部類的方式,不是很懂這種寫法的優(yōu)點,查了一下各種寫法的優(yōu)缺點,總結(jié)一下。
內(nèi)容多處參考文章: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;
      }

      // 線程安全,效率低,只有一個線程能調(diào)用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();這句語句的執(zhí)行,不是一個原子操作,JVM在執(zhí)行這條語句時,做了3個操作。
  1. 給mInstance分配內(nèi)存。
  2. 調(diào)用Single的構(gòu)造方法進行初始化。
  3. 將mInstance對象指向分配的內(nèi)存空間(執(zhí)行完這步mInstance就非空啦)。

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

餓漢式

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

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

缺點

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

靜態(tài)內(nèi)部類

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 版本。

總結(jié)

單例模式最好采用靜態(tài)內(nèi)部類實現(xiàn),但是如果對懶加載參數(shù)沒有要求,餓漢式也可以。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容