單例模式(Singleton Pattern)典型的四種實現方式
阿新 • • 發佈:2018-12-29
一、簡介:
單例模式(Singleton Pattern)是一種常用的軟體設計模式。《設計模式》一書中對其介紹:“保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。”
在某些時候,一個系統只有一個例項是很重要且必要的,比如:Windows系統中的工作管理員, 同時只能存在一個,因為系統瞬態是固定的,存在多個工作管理員也是在浪費資源。
優點:
- 在記憶體裡只有一個例項,減少了記憶體的開銷,尤其是頻繁的建立和銷燬例項(比如管理學院首頁頁面快取)。
- 避免對資源的多重佔用(比如寫檔案操作)。
缺點:
- 沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來例項化。
二、單例模式的實現(Java):
- 這裡只介紹典型的四種實現方式,懶漢、餓漢、雙檢鎖、登記式/靜態內部類。至於其他變種也都是在此基礎上變化的,看多了反而凌亂。
- 實現單例注意3個點:1.單例類只能有一個例項(建構函式私有化)。2.單例類必須自己建立自己的唯一例項。3.暴露給外部一個訪問點。
1.懶漢模式:
- 多個執行緒可能同時進入if中,最終會建立多個例項,單執行緒情況下可以使用
- 懶漢模式執行緒不安全,但是有人會想,直接在getInstance()方法加個synchronized
/**
* 延遲載入,執行緒不安全
*/
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
2.餓漢模式:
- 基於 ClassLoader 機制避免了多執行緒的同步問題,但是在類載入時就建立了例項,浪費了記憶體。
- java中的Runtime類就是一個典型的單例實現,它記錄了java程式執行時的環境,且允許應用程式與應用程式正在執行的環境進行互動。
/**
* 無延遲載入,執行緒安全
*/
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
3.雙檢鎖模式:
- 這種方式採用雙鎖機制,安全且在多執行緒情況下能保持高效能。
- volatile 關鍵字作用主要是防止 instance = new Singleton() 指令重排,因為這句程式碼不是一個原子操作,而是①new Singleton()堆中分配記憶體②初始化Singleton成員變數③instance指向記憶體中剛開闢出的空間,如果不加volatile關鍵字,那麼執行順序①②③,由於指令重排可能變為①③②,當執行完①③之後,如果被另一個執行緒搶佔了資源,那麼判斷instance==null時,結果為false,然後會直接返回instance,此時未初始化,導致使用時可能會報錯。 -
/**
* 延遲載入,執行緒安全
*/
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
//第一次檢測,避免每次呼叫getInstance()時都需要獲取同步鎖
//提高了效率
if(instance==null){
//this
synchronized(Singleton.class){
//第二次檢測,保證多執行緒下安全,因為可能多個
//執行緒同時進入this位置。
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
4.登記式/靜態內部類:
- 這種方式同樣利用了 classloder 機制來保證初始化 instance 時只有一個執行緒,因為 SingletonHolder 類沒有被主動使用,只有通過顯式呼叫 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而例項化 instance。
/**
* 延遲載入 多執行緒安全
*/
private Singleton(){}
private static class InnerSingleton{
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance(){
return InnerSingleton.INSTANCE;
}