「乾貨分享」經典設計模式之單例模式

設計模式千千萬,總是單例最常見。

單例模式的定義

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

六種單例的創建方式

1.餓漢式

public class Singleton {
  private static Singleton instance = new Singleton();
  private Singleton() {}
  public static Singleton getInstance() {
    return instance;
  }
}

優點: 基於類的加載機制,避免了多線程同步問題,加載速度快。

缺點: 在類加載的時候就完成初始化,沒有懶加載,如果沒有使用這個實例,會造成內存浪費。

2.懶漢式-線程不安全版

public class Singleton {
  private static Singleton instance;
  private Singleton() {}
  public static Singleton getInstance() {
    if(instance == null){
      instance = new Singleton();
    }
    return instance;
  }
}

優點: 第一次調用是才初始化對象,避免浪費資源

缺點: 加載速度慢,線程不安全

3.懶漢式-線程安全版(synchronized加鎖)

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

優點: 多線程中保證線程安全 缺點: 每次獲取對象實例,都需要進行同步,造成不必要的同步開銷。

4.雙重校驗鎖

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

優點: 線程安全,懶加載,減少同步開銷

缺點: 第一個獲取對象速度稍慢,但其在某些情況下也會出現失效的情況,並不是完美的方式。

這裏面使用了兩次判空:第一次爲了不必要的加鎖同步,第二次是確保在instance爲null的情況下才創建實例,避免多次創建。

方法中還是用了關鍵字volatile對變量進行修飾,有如下幾個作用:

1.在Java內存模型中volatile可以保證可見性,及防止程序指令重排序。

2.對象的創建分爲如下幾個步驟:

instance = new Singleton();
  • 1.爲instance分配內存空間
  • 2.初始化instance
  • 3.將instance指向內存地址

如果不加volatile的話,程序的執行順序就可能變成1->3->2,多線程中就會導致線程獲取一個沒有初始化的實例。例如線程a 執行了1,3, 此時線程b調用getInstance()發現instance不爲空,返回instance,但此時instance還未初始化。

5.靜態內部類

public class Singleton {
  private Singleton() {}
  public static Singleton getInstance() {
    return SingletonHolder.sInstance;
  } 
  private static class SingletonHolder {
    private static final Singleton sInstance = new Singleton();
  }
}

第一次加載類的時候不會初始化instance,只有第一次調用getInstance()的時候纔會進行加載SingleHolder並初始化instance,保證線程安全,也能保證實例唯一,推薦使用這種方式。

6.枚舉

public enum Singleton {
INSTANCE;
public void doSomeThing(){}
}

默認枚舉單例的創建是線程安全的,並且任何情況下都是單例。

以上就是6中常見的單例創建形式,按需使用吧。

單例的使用場景

  • 整個項目需要一個共享訪問點或者數據
  • 創建一個對象需要耗費的資源太多,比如訪問數據庫資源等
  • 工具類對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章