基本概念
- 意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點
- 主要成分:
a. 私有靜態成員變量
b. 私有構造器
c. 公有靜態get方法 - UML圖
實現方法
餓漢模式
tip1:在類準備階段中實現變量初始化,直接通過get方法獲取初始化後的類變量
class Singleton{
private static Singleton instance=new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
懶漢模式
- 非線程安全版本
tip1:只適用於單線程,多線程不適用
多線程不適用的原因:
step1:初始instance爲null
step2:多個線程同時訪問getInstance()方法
step3:多個線程的條件判斷都滿足(即instance等於null),於是創建了兩個或多個Singleton對象
summary:非線程安全版本在多線程時,違背了Singleton模式的初衷
class Singleton{
private static Singleton instance=null;
private Singleton() {}
public static Singleton getInstance() {
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
- 線程安全版本
tip1:對getInstance()加鎖
tip2:鎖的代價過高(多個線程同時讀,沒必要加鎖),因此不適用於高併發的情況,如Web開發
實現多線程的流程:假設有多個線程A、B…
step1:初始instance爲null
step1:多個線程同時訪問getInstance()方法
step2:最先訪問的線程A先向下執行,並對getInstance()方法 加鎖,此時其他線程無法向下執行
step3:線程A中符合條件instance等於null,調用構造器,instance不等於空
step4:…直到線程A釋放鎖,其他方法才能使用getInstance()方法
class Singleton{
private static Singleton instance=null;
private Singleton() {}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if(instance==null) {
instance=new Singleton();
}
}
return instance;
}
}
- 雙檢查鎖(double check)
tip1:鎖前檢查,避免了線程安全版本代價過高的問題
tip2:鎖後檢查,避免了多個線程創建多個對象的問題
對於instance=new Singleton();語句,在計算機中的實現可分爲如下3個步驟
stepA:分配內存
stepB:調用構造器,對分配的內存進行初始化
stepC:將內存的地址給instance
但是由於內存讀寫reoder(重排列優化問題)不安全,使得該實現步驟出現stepA、stepC、stepB的情況
從而使得多個線程的instance僅僅拿到一個原生的地址(即不含內容)
class Singleton{
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance==null) {//鎖前檢查
synchronized (Singleton.class) {
if(instance==null) {//鎖後檢查
instance=new Singleton();
}
}
}
return instance;
}
}
- 改進的雙檢查鎖
tip1:對變量instance增加volatile聲明。表示編譯器不能對其進行優化,則可以保證instance的整個構造過程按stepA、stepB、stepC順序執行
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;
}
}