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;
}
}
可以實現延遲載入,又可以保證執行緒安全,不影響系統性能
單例模式總結:
待續