延遲加載的一些知識和誤區(qū)

原文地址https://www.hcyhj.cn/2018/11/21/delay-load


最近開始看《java并發(fā)編程的藝術(shù)》一書,從里面get到了好些知識上的盲點,下面就延遲加載這個問題來分析一波~~


首先咱們來看一段簡單的代碼:

public class DelayLoad {

    private DelayLoad() {
    }

    private static DelayLoad instance;

    public static DelayLoad getInstance() {
        if (instance == null) {               //步驟1
            instance = new DelayLoad();       //步驟2
        }
        return instance;
    }
}

從上面的代碼片段里,很容易發(fā)現(xiàn)在多線程并發(fā)情況下去調(diào)用getInstance是會出問題的.當(dāng)A線程和B線程同時進入到步驟1處,便會實例化兩個對象出來,A和B訪問到的對象就不會是同一個。


下面升級一下,加上同步關(guān)鍵字synchronized

public class DelayLoad {

    private DelayLoad() {
    }

    private static DelayLoad instance;

    public static synchronized DelayLoad getInstance() {
        if (instance == null) {
            instance = new DelayLoad();
        }
        return instance;
    }
}

代碼改成這樣后,可以完全保證并發(fā)情況下獲取的instance實例都會是同一個,但是多個線程同時調(diào)用synchronized 修飾的方法,會有獲取鎖以及釋放鎖操作,這里會造成大量的性能損耗,得不償失!


繼續(xù)改造一下,看能不能提升下性能:

public class DelayLoad {

    private DelayLoad() {
    }

    private static DelayLoad instance;

    public static  DelayLoad getInstance() {
        if (instance == null) {                     //第一次檢查
            synchronized (DelayLoad.class){
                if (instance == null) {             //第二次檢查
                    instance = new DelayLoad();    //創(chuàng)建實例
                }
            }
        }
        return instance;
    }
}

咱們這里用雙重檢測的方法來實現(xiàn)這個單例懶加載,用這種策略看上去貌似沒有什么問題,多線程并發(fā)的情況下往往也就是在第一次檢查時都會直接返回實例,這樣就不會造成性能損耗.但是,這里有可能出現(xiàn)instance不一致的問題掌呜。對于這個問題我們得先了解對象的初始化過程

對象的初始化過程

1.在堆上為DelayLoad對象分配足夠大的空間,所有屬性和方法都被設(shè)置成缺省值(數(shù)字為0详拙,字符為null,布爾為false蔓同,而所有引用被設(shè)置成null)饶辙。
2.執(zhí)行構(gòu)造函數(shù)檢查是否有父類,如果有父類會先調(diào)用父類的構(gòu)造函數(shù)斑粱,這里假設(shè)DelayLoad沒有父類弃揽,執(zhí)行缺省值字段的賦值即方法的初始化動作。
3.執(zhí)行構(gòu)造函數(shù).

上面創(chuàng)建實例的那一步在cpu上可能經(jīng)過如下操作:

memory = allocate(); //1分配對象內(nèi)存空間
initInstance(memory);//2初始化對象
instance = memory;  //3設(shè)置instance指向剛分配的內(nèi)存地址

但實際上執(zhí)行的過程中,2和3步驟有可能進行指令重排,也就是按132的順序執(zhí)行,這樣就會導(dǎo)致instance指向的是一個屬性和值都是缺省值的對象。然后被一個競爭線程所拿到并進行使用矿微。



目前有兩種解決辦法

第一種:給實例變量加上volatile 關(guān)鍵字修飾

public class DelayLoad {

    private DelayLoad() {
    }

    private static volatile DelayLoad instance;

    public static  DelayLoad getInstance() {
        if (instance == null) {
            synchronized (DelayLoad.class){
                if (instance == null) {
                    instance = new DelayLoad();
                }
            }
        }
        return instance;
    }
}

代碼改成上述情況后,在設(shè)置instance指向更分配的內(nèi)存地址之前會有StoreStore內(nèi)存屏障,執(zhí)行代碼會禁止指令重排,這樣咱們拿到的instance都是經(jīng)過初始化過的痕慢。

第二種:基于類初始化的解決方案

public class DelayLoad {

    private DelayLoad() {
    }

    private static class DelayLoadHolder {
        public static DelayLoad instance = new DelayLoad();
    }

    public static DelayLoad getInstance() {
        return DelayLoadHolder .instance;
    }
}

該延遲加載方案是基于JVM的類初始化原理實現(xiàn)的。在執(zhí)行類的初始化期間涌矢,JVM會去獲取一個鎖掖举,該鎖可以同步多個線程對同一個類的初始化。類只會被加載一次,在加載完成之前對其他線程都是不可見的娜庇。這樣也能保證獲取到的instance也是同一個拇泛。


End

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市思灌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恭取,老刑警劉巖泰偿,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜈垮,居然都是意外死亡耗跛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門攒发,熙熙樓的掌柜王于貴愁眉苦臉地迎上來调塌,“玉大人,你說我怎么就攤上這事惠猿「崂” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵偶妖,是天一觀的道長姜凄。 經(jīng)常有香客問我,道長趾访,這世上最難降的妖魔是什么态秧? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扼鞋,結(jié)果婚禮上申鱼,老公的妹妹穿的比我還像新娘。我一直安慰自己云头,他們只是感情好捐友,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盘寡,像睡著了一般楚殿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天脆粥,我揣著相機與錄音砌溺,去河邊找鬼。 笑死变隔,一個胖子當(dāng)著我的面吹牛规伐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匣缘,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猖闪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肌厨?” 一聲冷哼從身側(cè)響起培慌,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柑爸,沒想到半個月后吵护,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡表鳍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年馅而,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片譬圣。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓮恭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厘熟,到底是詐尸還是另有隱情屯蹦,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布盯漂,位于F島的核電站颇玷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏就缆。R本人自食惡果不足惜帖渠,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竭宰。 院中可真熱鬧空郊,春花似錦、人聲如沸切揭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廓旬。三九已至哼审,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涩盾。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工十气, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人春霍。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓砸西,卻偏偏與公主長得像,于是被迫代替她去往敵國和親址儒。 傳聞我的和親對象是個殘疾皇子芹枷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容