單例模式的一些版本及演變過程
@import url(http://www.aygfsteel.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); @import url(http://www.aygfsteel.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); @import url(http://www.aygfsteel.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);版本一:只支持單線程,多線程第一次實(shí)例化會(huì)有兩個(gè)實(shí)例生成
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}
// other functions and members...
} 版本二:多線程版本:每個(gè)線程過來都會(huì)到要到synchronized 方法塊,這樣處理效率較低.第一個(gè)初始化helper的時(shí)候需要locking(加鎖),而后面取用helper的時(shí)候,根本不需要線程同步 // Correct multithreaded version
class Foo {
private Helper helper = null;
public synchronized Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}
// other functions and members...
} 版本三:解決每次新建實(shí)例都要synchronized的問題,運(yùn)用雙檢鎖來實(shí)現(xiàn).此種方法行不通
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}
思路很簡(jiǎn)單,就是我們只需要同步(synchronize)初始化helper的那部分代碼從而使代碼既正確又很有效率。
這就是所謂的“雙檢鎖”機(jī)制(顧名思義)。
很可惜,這樣的寫法在很多平臺(tái)和優(yōu)化編譯器上是錯(cuò)誤的。
原因在于:helper = new Helper()這行代碼在不同編譯器上的行為是無法預(yù)知的。一個(gè)優(yōu)化編譯器可以合法地如下實(shí)現(xiàn)helper = new Helper():
1. helper = 給新的實(shí)體分配內(nèi)存
2. 調(diào)用helper的構(gòu)造函數(shù)來初始化helper的成員變量
現(xiàn)在想象一下有線程A和B在調(diào)用getHelper,線程A先進(jìn)入,在執(zhí)行到步驟1的時(shí)候被踢出了cpu。然后線程B進(jìn)入,B看到的是 helper已經(jīng)不是null了(內(nèi)存已經(jīng)分配),于是它開始放心地使用helper,但這個(gè)是錯(cuò)誤的,因?yàn)樵谶@一時(shí)刻,helper的成員變量還都是缺 省值,A還沒有來得及執(zhí)行步驟2來完成helper的初始化。
當(dāng)然編譯器也可以這樣實(shí)現(xiàn):
1. temp = 分配內(nèi)存
2. 調(diào)用temp的構(gòu)造函數(shù)
3. helper = temp
如果編譯器的行為是這樣的話我們似乎就沒有問題了,但事實(shí)卻不是那么簡(jiǎn)單,因?yàn)槲覀儫o法知道某個(gè)編譯器具體是怎么做的,因?yàn)樵贘ava的 memory model里對(duì)這個(gè)問題沒有定義(C++也一樣),而事實(shí)上有很多編譯器都是用第一種方法(比如symantec的just-in-time compiler),因?yàn)榈谝环N方法看起來更自然。
在上面的參考文章中還提到了更復(fù)雜的修改方法,不過很可惜,都是錯(cuò)誤的
版本四:目前知道最后版。實(shí)際應(yīng)用時(shí)需驗(yàn)證一下
關(guān)于Out-of-order writes現(xiàn)象,就是
helper = new Helper();;
helper已經(jīng)非空,但對(duì)象還沒完成實(shí)例化,即new new Helper()未完成
詳見:
http://www.ibm.com/developerworks/java/library/j-dcl.html
版本四:目前知道最后版。實(shí)際應(yīng)用時(shí)需驗(yàn)證一下
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
因?yàn)榇嬖贠ut-of-order writes現(xiàn)象,所以這里volatile關(guān)鍵字是在當(dāng)instance被初始化給Singleton實(shí)例時(shí)保證多線程正確地處理instance變量,那這里與線程間的可見性有關(guān)嗎?
我覺得與可見性無關(guān),因?yàn)閟ynchronized block已經(jīng)可以保證塊內(nèi)變量的可見性,這里應(yīng)該是變量操作的原子性
http://en.wikipedia.org/wiki/Double-checked_locking
http://en.wikipedia.org/wiki/Double-checked_locking
posted on 2011-11-21 15:28 tobyxiong 閱讀(232) 評(píng)論(0) 編輯 收藏 所屬分類: java