1. 程式人生 > >sincerit 單例模式(Singleton Pattern)

sincerit 單例模式(Singleton Pattern)

先引入一個問題:
Windows工作管理員大家都很熟悉,大家可以嘗試在計算器上多次單擊“啟動工作管理員”,看是否可以啟動多個工作管理員,在正常情況下,無論啟動工作管理員多少次,Windows始終只能彈出一個工作管理員的視窗,也就是說,在一個Windows系統中,工作管理員只存在一個例項,為什麼要這樣設計呢?
可以從以下兩個方面來分析:
其一,如果能彈出多個視窗,且這些視窗的內容完全一致,全部都是重複物件,這必然會浪費系統資源,而且這根本沒必要顯示這麼多個內容完全相同的視窗。
其二,如果彈出的多個視窗內容不一致,問題就更嚴重了,這意味著在某一瞬間系統資源使用的情況和程序,服務等資訊存在多種狀態,例如工作管理員A顯示CPU使用率為10%,視窗B顯示CPU使用率為15%,到底哪個是真實的呢,這會給使用者帶來誤解,更不可取,由此可見,確保Windows工作管理員在系統中有且僅有一個是非常重要的,在實際開發中,有一些類的設計其物件是要保證是唯一的,這就是單例模式的目的所在。

學習時遇到的幾個問題
如何保證建立的例項是唯一的?
在建立例項時如何保證是執行緒安全的(同一時刻多執行緒訪問管理器時保證只能訪問唯一的一個管理器)

單例模式
單例模式:確保一個類(Singleton)只有一個例項,而且自行例項化並向整個系統提供這個例項,這個類稱為單例類,它提供了全域性訪問方法,是一種物件建立型模式

單例模式結構圖:
在這裡插入圖片描述

單例模式例項物件唯一性建立的方法:

class TaskManager {
  private static TaskManager tm = null; // 聚合的體現:內建一個本身的物件
  private TaskManager() {
// private使無法在類外面new出例項 .... } public static TaskManager getInstance() { // 暴露出一個可以訪問的方法 if (tm == null) { tm = new TaskManager(); } return tm; } }

上面一個是最簡單的單例類的設計,其物件在類外是無法被new出來的,因為它的構造方法是私有的,如果要呼叫其物件是隻能通過getInstance()獲取,第一次呼叫getInstance()時就會建立一個物件例項,再次呼叫時就不會建立新的物件了返回第一次建立的物件,這就保證了物件的唯一性

關於自建立例項物件的方法:
餓漢式單例類

class EagerSingleton {
  private static final EagerSingleton instance = new EagerSingleton(); // final相當於c裡面的const
  private EagerSingleton(){}
  public static EagerSingleton getInstance() {
    return instance;
  }
}

餓漢式就是在類載入的時候就已經建立好了例項,就等執行緒來呼叫了
優點:保證了執行緒的安全
缺點:在載入類的時候就建立物件,會影響載入的效率

懶漢式單例類

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

懶漢式在載入類的時候不自行例項化物件,而是在要得到物件的時候才去例項化
這種技術又稱為延遲載入技術(Lazy Load), 即需要的時候再建立例項,
該方法要使用關鍵詞sychronized,避免多個執行緒同時呼叫getInstance()

缺點:雖然使用同步鎖解決了執行緒安全問題,但每次呼叫getInstance使都需要進行執行緒鎖定判斷,再多執行緒高併發訪問環境中,將導致系統性能降低。

對於懶漢式的方法進行改進

class LazySingleton {
  private volatile static LazySingleton instance = null;
  private LazySingleton(){}
  public static LazySingleton getInstance() {
    if (instance == null) {
      sychronized (LazySingleton.class) {
          if (instance == null)  {
             instance = new LazySingleton(); // 建立單例例項
          }
       }
    }
    return instance;
  }
}

該方法使用的是雙重檢查鎖定
在instance前新增volatile,防止編譯器在編譯優化的時候吧兩個if優化成一個, 使用volatile
關鍵字會遮蔽Java虛擬機器所做的一些優化

靜態內部類建立(推薦使用)

class Singleton {
  private Singleton(){}
  private static class HolderClass { // 靜態內部類
    private final static Singleton instance = new Singleton();
  }
  public static Singleton getInstance() {
     return HolderClass.instance;
   }
}

可以實現延遲載入,又可以保證執行緒安全,不影響系統性能

單例模式總結:
待續