單執行緒下的單例模式:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton()
}
return instance;
}
}
幾個關鍵點:
- static 修飾:表名屬於類而不是類物件,不會每生成一個新的類物件都新生成一份。並且可以在不建立類物件的情況下直接呼叫。
- 為什麼建構函式是
private
型別?不然呢,開放了建構函式還怎麼單例。 - 為什麼不把單例的邏輯放到建構函式中?在
Singleton()
中呼叫Singleton()
麼,那不是死迴圈了。 - 類中的單例變數是
private
型別的,不能直接訪問,要透過getInstance()
來獲取。
多執行緒下的單例模式:
和單執行緒有什麼區別?
- 需要考慮執行緒安全問題
- 需要考慮效率問題
方法一:
只需要給 getInstance
方法新增 synchronized
關鍵字即可。
public static synchronized Singleton getInstance() {
問題:每次訪問都要同步,會降低效能。
方法二:
雙重檢查鎖定
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton()
}
}
}
return instance;
}
}
關鍵點:
- 效能最佳化:將
synchronized
放到實際建立時,只有第一次例項未建立才會同步,後續都不會。 - 為什麼要雙重檢查
instance==null
?第一次檢查完,有可能被別的執行緒先建立了。 - 為什麼
instance
要用volatile
修飾?- 因為
new Singleton()
不是一個單一的操作,會存在指令重排的問題。 - 1、為
instance
分配記憶體空間。2、初始化instance
。3、將instance
指向分配的記憶體地址。 - 如果指令重拍後,變為了 1-3-2,那麼其他執行緒可能會拿到一個還沒初始化的
instance
。
- 因為
- 為什麼有了
volatile
還需要synchronized
?- 因為
volatile
不保證原子性。
- 因為