設計模式之單例DCL爲什麼需要volatile

單例模式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可實現禁止指令重排,防止上述情況發生。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章