Posted on 2008-09-14 15:40
Lv Yuanfang 閱讀(443)
評論(0) 編輯 收藏
多線程環境下的單例模式實現
----Head First Pattern之單例模式
單例模式我想大家都比較熟悉,就是在JVM運行期間一個類只有一個實例,任何時候都是取得同一個實例,也就是一個全局變量了。
單例模式分懶漢式和餓漢式,但是懶漢式的單例在多線程環境下會有同步的問題,下面詳細介紹了用3中方法來解決此問題。
單例模式具有以下幾個特點:
1.JVM運行期間有且只有一個實例
2.構造函數是私有的
3.通過一個靜態工廠方法來獲得唯一的實例
4.累內部有一個私有靜態實例,通過靜態工廠方法創建后,每次再調用靜態工廠方法,返回的都是同一個實例
餓漢式:
public class Singleton{
??? private static Singleton uniqueInstance = new Singleton();
??? // 其他實例變量
??? private Singleton(){}
??? public static Singleton getInstance(){
?? ???? return uniqueInstance;
?? ?}
?? ?
??? // 其他方法
}
懶漢式:
public class Singleton{
??? private static Singleton uniqueInstance;
??? // 其他實例變量
??? private Singleton(){}
??? public static Singleton getInstance(){
?? ???? if(uniqueInstance == null){
?? ??? ???? uniqueInstance = new Signleton();
?? ??? ?}
?? ???? return uniqueInstance;
?? ?}
?? ?
??? // 其他方法
}
多線程環境下的單例模式:
上面的代碼就是最基本的單例模式示例代碼。但是懶漢式單例有一個問題,因為要保證有且僅有一個實例,如果在多線程環境下調用Singleton.getInstance(),就可能會有多個實例!為了解決多線程訪問的問題,有3種解決方法供選擇:
1.靜態工廠方法加同步關鍵字,這種方法是在對性能要求不高的情況下采用。
public class Singleton{
??? private static Singleton uniqueInstance;
??? // 其他實例變量
??? private Singleton(){}
??? public static synchronised Singleton getInstance(){
?? ???? if(uniqueInstance == null){
?? ??? ???? uniqueInstance = new Signleton();
?? ??? ?}
?? ???? return uniqueInstance;
?? ?}
?? ?
??? // 其他方法
}
2.始終用餓漢式單例
public class Singleton{
??? private static Singleton uniqueInstance = new Singleton();
??? // 其他實例變量
??? private Singleton(){}
??? public static Singleton getInstance(){
?? ???? return uniqueInstance;
?? ?}
?? ?
??? // 其他方法
}
餓漢式的方法,會依賴于JVM在加載類的時候,就創建唯一的實例。在每個線程訪問getInstance方法前,唯一實例已經被創建。
3.用雙檢查鎖來減少懶漢式中靜態方法getInstance的同步開銷
對public static synchronised Singleton getInstance()的每次調用,都需要同步,而雙檢查鎖的方式只是在第一次創建實例時同步,其他時候并不需要同步。
public class Singleton{
??? private volatile static Singleton uniqueInstance;
??? private Singleton(){}
??? public static Singleton getInstance(){
?? ???? if(uniqueInstance == null){
?? ??? ???? synchronised(Singleton.class){
?? ??? ??? ???? if(uniqueInstance == null){
?? ??? ??? ??? ??? ??? uniqueInstance = new Singleton();
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ???? return uniqueInstance;
?? ?}
}
如果調用時實例為null,則進入同步區塊,此時再進行判斷,如果還為null,就創建唯一的實例。有可能在一個線程在 if(uniqueInstance == null) 后進入同步區塊前,另一個線程恰好已經創建成功并從同步區塊中出來,這就需要進入同步區塊后,再做uniqueInstance是否為null的判斷。
同時uniqueInstance需要加volatile關鍵字,保證在創建單例實例時,多個線程能正確處理uniqueInstance變量。
注意:
雙檢查鎖的方式在Java1.4及1.4以前版本不能工作!!因此雙檢查鎖只能在Java 5及以上版本才可以使用。
記得Effictive Java中也提到過雙檢查鎖,也說不能在Java1.4中使用。
原因是Java 1.4及以前的JVM中對volatile關鍵字的實現允許對雙檢查鎖不合適的同步。(誰能幫我再深入解釋下?)原文是:
Unfortunately, in Java version 1.4 and earlier, many JVMs contain implementations of the volatile keyword that allow improper synchronization for double-checked locking. If you must use a JVM other than Java 5, consider other methods of implementing your Singleton.