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