【多線程】懶漢模式-DCL+volatile

double-checked locking、雙檢鎖、DCL機(jī)制還是存在線程安全的問題。
原因是new 操作不是原子操作,存在指令重排的問題
某一個線程在執(zhí)行到第一次檢測,讀取到的instance不為null時,instance的引用對象可能沒有完成初始化.

一、解決方法:volatile,禁止指令重排

最終版本的單例模式

public class Singleton {
    // 1.持有自己類的屬性
    private static volatile Singleton instance;

    // 2.私有的構(gòu)造方法
    private Singleton() {
    }

    // 3.提供對外獲取實(shí)例的方法
    // DCL + volatile
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    // 因?yàn)?new 操作不是原子操作,所以需要對屬性加以volatile修飾,避免重排序
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

二、再分析

DCL(雙端檢鎖) 機(jī)制不一定線程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排
原因在于某一個線程在執(zhí)行到第一次檢測,讀取到的instance不為null時,instance的引用對象可能沒有完成初始化.
instance=new SingletonDem(); 可以分為以下步驟(偽代碼)
memory=allocate();//1.分配對象內(nèi)存空間
instance(memory);//2.初始化對象
instance=memory;//3.設(shè)置instance的指向剛分配的內(nèi)存地址,此時instance!=null

步驟2和步驟3不存在數(shù)據(jù)依賴關(guān)系.而且無論重排前還是重排后程序執(zhí)行的結(jié)果在單線程中并沒有改變,因此這種重排優(yōu)化是允許的.

memory=allocate();//1.分配對象內(nèi)存空間
instance=memory;//3.設(shè)置instance的指向剛分配的內(nèi)存地址,此時instance!=null 但對象還沒有初始化完.
instance(memory);//2.初始化對象

但是指令重排只會保證串行語義的執(zhí)行一致性(單線程) 并不會關(guān)心多線程間的語義一致性
所以當(dāng)一條線程訪問instance不為null時,由于instance實(shí)例未必完成初始化,也就造成了線程安全問題.

參考:互聯(lián)網(wǎng)大廠高頻重點(diǎn)面試題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容