簡(jiǎn)談雙重檢查鎖定以及其用途

單例類在Java開發(fā)者中非常常用,但是它給初級(jí)開發(fā)者們?cè)斐闪撕芏嗵魬?zhàn)叮雳。他們所面對(duì)的其中一個(gè)關(guān)鍵挑戰(zhàn)是想暗,怎樣確保單例類的行為是單例?也就是說帘不,無論任何原因说莫,如何防止單例類有多個(gè)實(shí)例。在整個(gè)應(yīng)用生命周期中寞焙,要保證只有一個(gè)單例類的實(shí)例被創(chuàng)建储狭,雙重檢查鎖(Double checked locking of Singleton)是一種實(shí)現(xiàn)方法。顧名思義捣郊,在雙重檢查鎖中辽狈,代碼會(huì)檢查兩次單例類是否有已存在的實(shí)例,一次加鎖一次不加鎖模她,一次確保不會(huì)有多個(gè)實(shí)例被創(chuàng)建稻艰。

順便提一下,在JDK1.5中侈净,Java修復(fù)了其內(nèi)存模型的問題尊勿。在JDK1.5之前僧凤,這種方法會(huì)有問題。本文中元扔,我們將會(huì)看到怎樣用Java實(shí)現(xiàn)雙重檢查鎖的單例類躯保,為什么Java 5之前的版本雙重檢查鎖會(huì)有問題,以及怎么解決這個(gè)問題澎语。順便說一下途事,這也是重要的面試要點(diǎn),我曾經(jīng)在金融業(yè)和服務(wù)業(yè)的公司面試被要求手寫雙重檢查鎖實(shí)現(xiàn)單例模式擅羞、相信我尸变,這很棘手,除非你清楚理解了你在做什么减俏。你也可以閱讀我的完整列表“單例模式設(shè)計(jì)問題”來更好的準(zhǔn)備面試召烂。

為什么你需要雙重檢查鎖來實(shí)現(xiàn)單例類?

一個(gè)常見情景娃承,單例類在多線程環(huán)境中違反契約奏夫。如果你要一個(gè)新手寫出單例模式,可能會(huì)得到下面的代碼:

private static Singleton _instance;

public static Singleton getInstance() {

if (_instance == null) {

_instance = new Singleton();

}

return _instance;

}


然后历筝,當(dāng)你指出這段代碼在超過一個(gè)線程并行被調(diào)用的時(shí)候會(huì)創(chuàng)建多個(gè)實(shí)例的問題時(shí)酗昼,他很可能會(huì)把整個(gè)getInstance()方法設(shè)為同步(synchronized),就像我們展示的第二段示例代碼getInstanceTS()方法一樣梳猪。盡管這樣做到了線程安全麻削,并且解決了多實(shí)例問題,但并不高效春弥。在任何調(diào)用這個(gè)方法的時(shí)候碟婆,你都需要承受同步帶來的性能開銷,然而同步只在第一次調(diào)用的時(shí)候才被需要惕稻,也就是單例類實(shí)例創(chuàng)建的時(shí)候。這將促使我們使用雙重檢查鎖模式(double checked locking pattern)蝙叛,一種只在臨界區(qū)代碼加鎖的方法俺祠。程序員稱其為雙重檢查鎖,因?yàn)闀?huì)有兩次檢查 _instance == null借帘,一次不加鎖蜘渣,另一次在同步塊上加鎖。這就是使用Java雙重檢查鎖的示例:

public static Singleton getInstanceDC() {

if (_instance == null) {? ? ? ? ? ? ? ? // Single Checked

synchronized (Singleton.class) {

if (_instance == null) {? ? ? ? // Double checked

_instance = new Singleton();

}

}

}

return _instance;

}

這個(gè)方法表面上看起來很完美肺然,你只需要付出一次同步塊的開銷蔫缸,但它依然有問題。除非你聲明_instance變量時(shí)使用了volatile關(guān)鍵字际起。沒有volatile修飾符拾碌,可能出現(xiàn)Java中的另一個(gè)線程看到個(gè)初始化了一半的_instance的情況吐葱,但使用了volatile變量后,就能保證先行發(fā)生關(guān)系(happens-before relationship)校翔。對(duì)于volatile變量_instance弟跑,所有的寫(write)都將先行發(fā)生于讀(read),在Java 5之前不是這樣防症,所以在這之前使用雙重檢查鎖有問題∶霞現(xiàn)在,有了先行發(fā)生的保障(happens-before guarantee)蔫敲,你可以安全地假設(shè)其會(huì)工作良好饲嗽。另外,這不是創(chuàng)建線程安全的單例模式的最好方法奈嘿,你可以使用枚舉實(shí)現(xiàn)單例模式貌虾,這種方法在實(shí)例創(chuàng)建時(shí)提供了內(nèi)置的線程安全。

這是個(gè)用Java創(chuàng)建線程安全單例模式的有爭(zhēng)議的方法指么,使用枚舉實(shí)現(xiàn)單例類更簡(jiǎn)單有效酝惧。我并不建議你像這樣實(shí)現(xiàn)單例模式,因?yàn)橛肑ava有許多更好的方式伯诬。但是晚唇,這個(gè)問題有歷史意義,也教授了并發(fā)是如何引入一些微妙錯(cuò)誤的盗似。正如之前所說哩陕,這是面試中非常重要的一點(diǎn)。在去參加任何Java面試之前赫舒,要練習(xí)手寫雙重檢查鎖實(shí)現(xiàn)單例類悍及。這將增強(qiáng)你發(fā)現(xiàn)Java程序員們所犯編碼錯(cuò)誤的洞察力。另外接癌,在現(xiàn)在的測(cè)試驅(qū)動(dòng)開發(fā)中心赶,單例模式由于難以被模擬其行為而被視為反模式(anti pattern),所以如果你是測(cè)試驅(qū)動(dòng)開發(fā)的開發(fā)者缺猛,最好避免使用單例模式缨叫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荔燎,隨后出現(xiàn)的幾起案子耻姥,更是在濱河造成了極大的恐慌,老刑警劉巖有咨,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琐簇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡座享,警方通過查閱死者的電腦和手機(jī)婉商,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門似忧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人据某,你說我怎么就攤上這事橡娄。” “怎么了癣籽?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵挽唉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我筷狼,道長(zhǎng)瓶籽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任埂材,我火速辦了婚禮塑顺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俏险。我一直安慰自己严拒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布竖独。 她就那樣靜靜地躺著裤唠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莹痢。 梳的紋絲不亂的頭發(fā)上种蘸,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音竞膳,去河邊找鬼航瞭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坦辟,可吹牛的內(nèi)容都是我干的刊侯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锉走,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼滔吠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挠日,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翰舌,沒想到半個(gè)月后嚣潜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡椅贱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年懂算,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了只冻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡计技,死狀恐怖喜德,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垮媒,我是刑警寧澤舍悯,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站睡雇,受9級(jí)特大地震影響萌衬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜它抱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一秕豫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧观蓄,春花似錦混移、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撮珠,卻和暖如春沮脖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芯急。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工勺届, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娶耍。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓免姿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親榕酒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胚膊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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