單例模式完全解析
本文將探討單例模式的各種情況,并給出相應(yīng)的建議。單例模式應(yīng)該是設(shè)計(jì)模式中比較簡(jiǎn)單的一個(gè),但是在多線程并發(fā)的環(huán)境下使用卻是不那么簡(jiǎn)單了。首先看最原始的單例模式。
?1?package?xylz.study.singleton;
?2?
?3?public?class?Singleton?{
?4?
?5?????private?static?Singleton?instance?=?null;
?6?
?7?????private?Singleton()?{
?8?????}
?9?
10?????public?static?Singleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????instance?=?new?Singleton();
13?????????}
14?????????return?instance;
15?????}
16?}
17?
?2?
?3?public?class?Singleton?{
?4?
?5?????private?static?Singleton?instance?=?null;
?6?
?7?????private?Singleton()?{
?8?????}
?9?
10?????public?static?Singleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????instance?=?new?Singleton();
13?????????}
14?????????return?instance;
15?????}
16?}
17?
顯然這個(gè)寫法在單線程環(huán)境下非常好,但是多線程會(huì)導(dǎo)致多個(gè)實(shí)例出現(xiàn),這個(gè)大家都能理解。
最簡(jiǎn)單的改造方式是添加一個(gè)同步鎖。
?1?package?xylz.study.singleton;
?2?
?3?public?class?SynchronizedSingleton?{
?4?
?5?????private?static?SynchronizedSingleton?instance?=?null;
?6?
?7?????private?SynchronizedSingleton()?{
?8?????}
?9?
10?????public?static?synchronized?SynchronizedSingleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????instance?=?new?SynchronizedSingleton();
13?????????}
14?????????return?instance;
15?????}
16?}
17?
?2?
?3?public?class?SynchronizedSingleton?{
?4?
?5?????private?static?SynchronizedSingleton?instance?=?null;
?6?
?7?????private?SynchronizedSingleton()?{
?8?????}
?9?
10?????public?static?synchronized?SynchronizedSingleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????instance?=?new?SynchronizedSingleton();
13?????????}
14?????????return?instance;
15?????}
16?}
17?
顯然上面的方法避免了并發(fā)的問題,但是由于我們只是在第一次構(gòu)造對(duì)象的時(shí)候才需要同步,以后就不再需要同步,所以這里不可避免的有性能開銷。于是將鎖去掉采用靜態(tài)的屬性來解決同步鎖的問題。
?1?package?xylz.study.singleton;
?2?
?3?public?class?StaticSingleton?{
?4?
?5?????private?static?StaticSingleton?instance?=?new?StaticSingleton();
?6?
?7?????private?StaticSingleton()?{
?8?????}
?9?
10?????public?static?StaticSingleton?getInstance()?{
11?????????return?instance;
12?????}
13?}
14?
?2?
?3?public?class?StaticSingleton?{
?4?
?5?????private?static?StaticSingleton?instance?=?new?StaticSingleton();
?6?
?7?????private?StaticSingleton()?{
?8?????}
?9?
10?????public?static?StaticSingleton?getInstance()?{
11?????????return?instance;
12?????}
13?}
14?
上面的方法既沒有鎖又解決了性能問題,看起來已經(jīng)滿足需求了。但是追求“完美”的程序員想延時(shí)加載對(duì)象,希望在第一次獲取的時(shí)候才構(gòu)造對(duì)象,于是大家非常聰明的進(jìn)行改造,也即非常出名的雙重檢查鎖機(jī)制出來了。
?1?package?xylz.study.singleton;
?2?
?3?public?class?DoubleLockSingleton?{
?4?
?5?????private?static?DoubleLockSingleton?instance?=?null;
?6?
?7?????private?DoubleLockSingleton()?{
?8?????}
?9?
10?????public?static?DoubleLockSingleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????synchronized?(DoubleLockSingleton.class)?{
13?????????????????if?(instance?==?null)?{
14?????????????????????instance?=?new?DoubleLockSingleton();
15?????????????????}
16?????????????}
17?????????}
18?????????return?instance;
19?????}
20?}
21?
?2?
?3?public?class?DoubleLockSingleton?{
?4?
?5?????private?static?DoubleLockSingleton?instance?=?null;
?6?
?7?????private?DoubleLockSingleton()?{
?8?????}
?9?
10?????public?static?DoubleLockSingleton?getInstance()?{
11?????????if?(instance?==?null)?{
12?????????????synchronized?(DoubleLockSingleton.class)?{
13?????????????????if?(instance?==?null)?{
14?????????????????????instance?=?new?DoubleLockSingleton();
15?????????????????}
16?????????????}
17?????????}
18?????????return?instance;
19?????}
20?}
21?
雙重鎖機(jī)制看起來非常巧妙的避免了上面的問題。但是真的是這樣的嗎?文章《雙重檢查鎖定及單例模式》中談到了非常多演變的雙重鎖機(jī)制帶來的問題,包括比較難以理解的指令重排序機(jī)制等。總之就是雙重檢查鎖機(jī)制仍然對(duì)導(dǎo)致錯(cuò)誤問題而不是性能問題。
于是繼續(xù)改造,某個(gè)牛人利用JVM的特性來解決上述問題,具體哪個(gè)牛人我忘記了,但是不是下面文章的作者。
(1)《Java theory and practice: Fixing the Java Memory Model, Part 2》
(2)《Initialize-On-Demand Holder Class and Singletons》
?1?package?xylz.study.singleton;
?2?
?3?public?class?HolderSingleton?{
?4?
?5?????private?static?class?HolderSingletonHolder?{
?6?
?7?????????static?HolderSingleton?instance?=?new?HolderSingleton();
?8?????}
?9?
10?????private?HolderSingleton()?{
11?????????//maybe?throw?an?Exception?when?doing?something?
12?????}
13?
14?????public?static?HolderSingleton?getInstance()?{
15?????????return?HolderSingletonHolder.instance;
16?????}
17?}
18?
?2?
?3?public?class?HolderSingleton?{
?4?
?5?????private?static?class?HolderSingletonHolder?{
?6?
?7?????????static?HolderSingleton?instance?=?new?HolderSingleton();
?8?????}
?9?
10?????private?HolderSingleton()?{
11?????????//maybe?throw?an?Exception?when?doing?something?
12?????}
13?
14?????public?static?HolderSingleton?getInstance()?{
15?????????return?HolderSingletonHolder.instance;
16?????}
17?}
18?
上述代碼看起來解決了上面單例模式遇到的所有問題,而且實(shí)際上工作的很好,沒有什么問題。但是卻有一個(gè)致命的問題,如果第11行拋出了一個(gè)異常,也就是第一次構(gòu)造函數(shù)失敗將導(dǎo)致永遠(yuǎn)無法再次得到構(gòu)建對(duì)象的機(jī)會(huì)。
使用下面的代碼測(cè)試下。
?1?package?xylz.study.singleton;
?2?
?3?public?class?HolderSingletonTest?{
?4?
?5?????private?static?class?HolderSingletonHolder?{
?6?
?7?????????static?HolderSingletonTest?instance?=?new?HolderSingletonTest();
?8?????}
?9?
10?????private?static?boolean?init?=?false;
11?????
12?????private?HolderSingletonTest()?{
13?????????//maybe?throw?an?Exception?when?doing?something?
14?????????if(!init)?{
15?????????????init=true;
16?????????????throw?new?RuntimeException("fail");
17?????????}
18?????}
19?
20?????public?static?HolderSingletonTest?getInstance()?{
21?????????return?HolderSingletonHolder.instance;
22?????}
23?????public?static?void?main(String[]?args)?{
24?????????for(int?i=0;i<3;i++)?{
25?????????????try?{
26?????????????????System.out.println(HolderSingletonTest.getInstance());
27?????????????}?catch?(Exception?e)?{
28?????????????????System.err.println("one->"+i);
29?????????????????e.printStackTrace();
30?????????????}catch(ExceptionInInitializerError?err)?{
31?????????????????System.err.println("two->"+i);
32?????????????????err.printStackTrace();
33?????????????}catch(Throwable?t)?{
34?????????????????System.err.println("three->"+i);
35?????????????????t.printStackTrace();
36?????????????}
37?????????}
38?????}
39?}
40?
很不幸將得到以下輸出:?2?
?3?public?class?HolderSingletonTest?{
?4?
?5?????private?static?class?HolderSingletonHolder?{
?6?
?7?????????static?HolderSingletonTest?instance?=?new?HolderSingletonTest();
?8?????}
?9?
10?????private?static?boolean?init?=?false;
11?????
12?????private?HolderSingletonTest()?{
13?????????//maybe?throw?an?Exception?when?doing?something?
14?????????if(!init)?{
15?????????????init=true;
16?????????????throw?new?RuntimeException("fail");
17?????????}
18?????}
19?
20?????public?static?HolderSingletonTest?getInstance()?{
21?????????return?HolderSingletonHolder.instance;
22?????}
23?????public?static?void?main(String[]?args)?{
24?????????for(int?i=0;i<3;i++)?{
25?????????????try?{
26?????????????????System.out.println(HolderSingletonTest.getInstance());
27?????????????}?catch?(Exception?e)?{
28?????????????????System.err.println("one->"+i);
29?????????????????e.printStackTrace();
30?????????????}catch(ExceptionInInitializerError?err)?{
31?????????????????System.err.println("two->"+i);
32?????????????????err.printStackTrace();
33?????????????}catch(Throwable?t)?{
34?????????????????System.err.println("three->"+i);
35?????????????????t.printStackTrace();
36?????????????}
37?????????}
38?????}
39?}
40?
?1?two->0
?2?java.lang.ExceptionInInitializerError
?3?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
?4?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
?5?Caused?by:?java.lang.RuntimeException:?fail
?6?????at?xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
?7?????at?xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
?8?????at?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
?9?????
?2?more
10?three->1
11?java.lang.NoClassDefFoundError:?Could?not?initialize?class?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
12?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
13?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
14?three->2
15?java.lang.NoClassDefFoundError:?Could?not?initialize?class?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
16?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
17?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
18?
?2?java.lang.ExceptionInInitializerError
?3?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
?4?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
?5?Caused?by:?java.lang.RuntimeException:?fail
?6?????at?xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
?7?????at?xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
?8?????at?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
?9?????

10?three->1
11?java.lang.NoClassDefFoundError:?Could?not?initialize?class?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
12?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
13?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
14?three->2
15?java.lang.NoClassDefFoundError:?Could?not?initialize?class?xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
16?????at?xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
17?????at?xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
18?
很顯然我們想著第一次加載失敗第二次能夠加載成功,非常不幸,JVM一旦加載某個(gè)類失敗將認(rèn)為此類的定義有問題,將來不再加載,這樣就導(dǎo)致我們沒有機(jī)會(huì)再加載。目前看起來沒有辦法避免此問題。如果要使用JVM的類加載特性就必須保證類加載一定正確,否則此問題將比并發(fā)和性能更嚴(yán)重。如果我們的類需要初始話那么就需要想其它辦法避免在構(gòu)造函數(shù)中完成。看起來像是又回到了老地方,難道不是么?
總之,結(jié)論是目前沒有一個(gè)十全十美的單例模式,而大多數(shù)情況下我們只需要滿足我們的需求就行,沒必有特意追求最“完美”解決方案。
原文[http://www.imxylz.cn/p/177.html]
posted on 2009-12-23 16:34 飛熊 閱讀(148) 評(píng)論(0) 編輯 收藏 所屬分類: JAVA基礎(chǔ)