面試的時候赏迟,常常會被問到這樣一個問題:請您寫出一個單例模式(Singleton Pattern)吧悼院。好吧仪吧,寫就寫死遭,這還不容易偿短。順手寫一個
?這種寫法就是所謂的?饑餓模式欣孤,每個對象在沒有使用之前就已經(jīng)初始化了。這就可能帶來潛在的性能問題:如果這個對象很大呢昔逗?沒有使用這個對象之前降传,就把它加載到了內(nèi)存中去是一種巨大的浪費。針對這種情況勾怒,我們可以對以上的代碼進行改進婆排,使用一種新的設(shè)計思想——?延遲加載(Lazy-load Singleton)。
這種寫法就是所謂的?懶漢模式笔链。它使用了延遲加載來保證對象在沒有使用之前段只,是不會進行初始化的。但是鉴扫,通常這個時候面試官又會提問新的問題來刁難一下翼悴。他會問:這種寫法線程安全嗎?回答必然是:不安全幔妨。這是因為在多個線程可能同時運行到第九行鹦赎,判斷singObj為null,于是同時進行了初始化误堡。所以古话,這是面臨的問題是如何使得這個代碼線程安全?很簡單锁施,在那個方法前面加一個Synchronized就OK了陪踩。
寫到這里,面試官可能仍然會狡猾的看了你一眼悉抵,繼續(xù)刁難到:這個寫法有沒有什么性能問題呢肩狂?答案肯定是有的!?同步的代價必然會一定程度的使程序的并發(fā)度降低姥饰。那么有沒有什么方法傻谁,一方面是線程安全的,有可以有很高的并發(fā)度呢列粪?我們觀察到审磁,線程不安全的原因其實是在初始化對象的時候,所以岂座,可以想辦法把同步的粒度降低态蒂,只在初始化對象的時候進行同步。這里有必要提出一種新的設(shè)計思想——?雙重檢查鎖(Double-Checked Lock)费什。
這種寫法使得只有在加載新的對象進行同步钾恢,在加載完了之后,其他線程在第九行就可以判斷跳過鎖的的代價直接到第15行代碼了。做到很好的并發(fā)度瘩蚪。
至此刑桑,上面的寫法一方面實現(xiàn)了Lazy-Load,另一個方面也做到了并發(fā)度很好的線程安全募舟,一切看上很完美祠斧。這是,面試官可能會對你的回答滿意的點點頭拱礁。但是琢锋,你此時提出說,其實這種寫法還是有問題的D卦睢吴超!問題在哪里?假設(shè)線程A執(zhí)行到了第9行鸯乃,它判斷對象為空鲸阻,于是線程A執(zhí)行到第12行去初始化這個對象,但初始化是需要耗費時間的缨睡,但是這個對象的地址其實已經(jīng)存在了鸟悴。此時線程B也執(zhí)行到了第九行,它判斷不為空奖年,于是直接跳到15行得到了這個對象细诸。但是,這個對象還?沒有被完整的初始化陋守!得到一個沒有初始化完全的對象有什么用U鸸蟆!關(guān)于這個Double-Checked Lock的討論有很多水评,目前公認這是一個Anti-Pattern猩系,不推薦使用!所以當(dāng)你的面試官聽到你的這番答復(fù)中燥,他會不會被Hold住呢寇甸?
那么有沒有什么更好的寫法呢?有褪那!這里又要提出一種新的模式——?Initialization on Demand Holder.?這種方法使用內(nèi)部類來做到延遲加載對象幽纷,在初始化這個內(nèi)部類的時候,JLS(Java Language Sepcification)會保證這個類的線程安全博敬。這種寫法最大的美在于,完全使用了Java虛擬機的機制進行同步保證峰尝,沒有一個同步的關(guān)鍵字偏窝。
至此,本文完。提供一些鏈接For your reference:
Double-Checked Lock:?http://en.wikipedia.org/wiki/Double-checked_locking
Initialzation on Demand Holder:?http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom
本文轉(zhuǎn)載自:http://blog.sina.com.cn/s/blog_75247c770100yxpb.html