Singleton 單例設計模式

wangchao96發表於2018-08-30

什麼是單例模式?

概念

單例模式是一種常見的設計模式,可以使得某個類只有一個例項,自己建立該類例項並向外界提供該例項。

單例的三要素:

  • 私有構造器(防止客戶端例項化)
  • 靜態屬性
  • 靜態方法(同於獲取單例例項)

單例模式優缺點

優點

  • 資料共享
  • 專案安全(配合同步實現)

缺點

  • 併發性降低
  • 增加耦合

為什麼使用單例模式?

在某些條件下,使用單例模式可以控制類例項的數量,達到節約資源的目的,同時由於只有一個例項,可以實現通訊的功能以及資源共享,不僅如此,單例模式配合執行緒的同步可解決併發訪問中的安全問題。

在一些優先考慮安全性的程式中可以嘗試使用單例。

單例模式的缺點是非常明顯的,降低程式的併發性,增加模組之間的耦合,所以單例的使用需要謹慎。

幾種實現方式

根據例項建立的時機可以分為兩類:lazy-load、非lazy-load。

非lazy-load

/**
 * 單例模式
 * 當類載入時建立單例例項
 *
 * 優點:不需要考慮併發
 * 缺點:類載入後不獲取單例物件會造成記憶體浪費
 */
public final class IvoryTower {

  /**
   * 私有構造器
   */
  private IvoryTower() {}

  /**
   * 靜態屬性
   */
  private static final IvoryTower INSTANCE = new IvoryTower();

  /**
   * 靜態方法
   *
   * @return 單例例項
   */
  public static IvoryTower getInstance() {
    return INSTANCE;
  }
}
複製程式碼

非lazy-load

執行緒安全的單例類

/**
 * 執行緒安全的單例類
 * 例項是惰性初始化的,因此需要同步
 *
 * 注意:如果通過反射建立,那麼在同一個類載入器中不會建立單例物件,而是多個例項
 */
public final class ThreadSafeLazyLoadedIvoryTower {

  private static ThreadSafeLazyLoadedIvoryTower instance;

  private ThreadSafeLazyLoadedIvoryTower() {
    // 防止通過反射例項化
    if (instance == null) {
      instance = this;
    } else {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /**
   * 只有在第一次呼叫例項時才會建立例項。延遲載入
   */
  public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
    if (instance == null) {
      instance = new ThreadSafeLazyLoadedIvoryTower();
    }

    return instance;
  }
}
複製程式碼

靜態內部類

/**
 * 利用靜態內部類實現lazy-load的單例模式
 */
public final class InitializingOnDemandHolderIdiom {

  /**
   * 私有構造器
   */
  private InitializingOnDemandHolderIdiom() {}

  /**
   * 靜態方法
   *
   * @return 單例例項
   */
  public static InitializingOnDemandHolderIdiom getInstance() {
    return HelperHolder.INSTANCE;
  }

  /**
   * 靜態內部類
   */
  private static class HelperHolder {
    private static final InitializingOnDemandHolderIdiom INSTANCE =
        new InitializingOnDemandHolderIdiom();
  }
}
複製程式碼

Double check locking

/**
 * Double check locking
 */
public final class ThreadSafeDoubleCheckLocking {

  private static volatile ThreadSafeDoubleCheckLocking instance;

  /**
   * 私有構造器
   */
  private ThreadSafeDoubleCheckLocking() {
    // 防止通過反射例項化
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /**
   * 靜態方法
   *
   * @return 單例例項
   */
  public static ThreadSafeDoubleCheckLocking getInstance() {
    // 區域性變數可以提高25%的效能
    // Joshua Bloch,《Effective Java,第二版》,第283-284頁
    ThreadSafeDoubleCheckLocking result = instance;
    if (result == null) {
      synchronized (ThreadSafeDoubleCheckLocking.class) {
        result = instance;
        if (result == null) {
          instance = result = new ThreadSafeDoubleCheckLocking();
        }
      }
    }
    return result;
  }
}
複製程式碼

相關文章