單例模式DCL實現
pulic class Singleton {
//私有構造函數,方式外部通過new創建對象
private Singleton () {
}
//類的內部聲明變量
//volatile防止指令重排
private static volatile Singleton singleton;
//對外暴露一個靜態方法,當調用該方法時,纔去創建實例(singleton)
//加入雙重檢查,解決線程安全問題,同時支持Lazy Loading,同時保證了效率
//推薦使用
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
不加volatile會有什麼問題?
程序執行過程中, 爲了性能考慮, 編譯器和CPU可能會對指令重新排序。
在極少數的情況下,下面語句會發生指令重排
singleton = new Singleton();
以上語句經過編譯後,可分爲以下三步(僞代碼)
memory = allocate(); // 1:分配對象的內存空間
instance(memory); // 2:初始化對象
instance = memory; // 3:設置instance指向剛分配的內存地址
以上步驟2與步驟3不存在數據依賴關係,而且無論重排前或者重排後程序的執行結果在單線程中沒有變化,因此這種重排優化是允許的。所以存在以下順序執行情況:
memory = allocate(); // 1:分配對象的內存空間
instance = memory; // 3:設置instance指向剛分配的內存地址
instance(memory); // 2:初始化對象
如果執行順序爲1.3.2,當A線程執行完1,3(此時還未完成對象初始化),這時B線程第一次判斷singleton == null,得到的結果未false,然後就會直接return一個空的對象,繼而產生後續錯誤。
爲什麼需要volatile?
volatile可實現禁止指令重排,防止上述情況發生。