個人整合學習自
- 《Java設計模式》 劉偉 編著
單例模式
Singleton Pattern:確保一個類職業一個實例,並提供一個全局訪問點來訪問這個唯一實例。也是一直創建型模式。
單例模式的三個要點:
- 某個類只能有一個實例;
- 它必須自行創建這個實例;
- 它必須自行向整個系統提供這個實例。
實現
1. 餓漢式單例
package xyz.cglzwz.designpattern.sp;
/**
* 餓漢式單例
* @author chgl16
* @date 2019-04-02
*/
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
當類加載時,靜態變量instance會被初始化,此時類的私有構造函數會被調用,單例類的唯一實例將被創建。(叫餓漢是eager的意思,渴望第一時間加載)
- 優點:無需考慮多線程同時訪問的問題,可以確保實例的唯一性,而且調用速度和反應時間比以下的懶漢模式要優。
- 缺點:類加載時就創建,加載時間可能比較長。
2. 懶漢式單例
package xyz.cglzwz.designpattern.sp;
/**
* 懶漢式單例模式
*
* @author chgl16
* @date 2019-04-02
*/
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
懶漢式都是在第一次調用getInstance()方法時初始化實例,在類加載時並不自行創建,這種技術也稱爲延遲加載(Lazy Load)技術。
上面代碼雖然增加了synchronized修飾符進行線程隱式鎖定,解決了線程安全問題。但是多線程高併發訪問環境還是會大大降低系統性能。因此可以代碼優化下,因爲這裏可見只需對 instance = new LazySingleton(); 鎖定即可。如下改進
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
}
問題貌似解決,但是並非如此。如果多個線程都同時到了 "if (instance == null) "爲 true的情況,都通過了判斷,由於事先了synchronized加鎖機制,只是某一個線程創建了對象。但是後面的線程不知道,會繼續創建的(因爲已經通過判斷了)。
繼續改進,使用雙重檢查鎖定
package xyz.cglzwz.designpattern.sp;
/**
* 懶漢模式雙重檢查鎖定
* @author chgl16
* @date 2019-04-03
*/
public class LazySingleton2 {
// 使用volatile修飾,確保每個線程能夠正確處理
private volatile static LazySingleton2 instance = null;
private LazySingleton2() {}
public static synchronized LazySingleton2 getInstance() {
// 第一重判斷
if (instance == null) {
// 鎖定代碼塊
synchronized (LazySingleton2.class) {
// 第二重判斷
if (instance == null) {
// 創建單例實例
instance = new LazySingleton2();
}
}
}
return instance;
}
}