單例模式完全解析

          本文將探討單例模式的各種情況,并給出相應的建議。單例模式應該是設計模式中比較簡單的一個,但是在多線程并發的環境下使用卻是不那么簡單了。
          首先看最原始的單例模式。
          ?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?

          顯然這個寫法在單線程環境下非常好,但是多線程會導致多個實例出現,這個大家都能理解。
          最簡單的改造方式是添加一個同步鎖。
          ?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?

          顯然上面的方法避免了并發的問題,但是由于我們只是在第一次構造對象的時候才需要同步,以后就不再需要同步,所以這里不可避免的有性能開銷。于是將鎖去掉采用靜態的屬性來解決同步鎖的問題。
          ?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?

          上面的方法既沒有鎖又解決了性能問題,看起來已經滿足需求了。但是追求“完美”的程序員想延時加載對象,希望在第一次獲取的時候才構造對象,于是大家非常聰明的進行改造,也即非常出名的雙重檢查鎖機制出來了。
          ?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?

          雙重鎖機制看起來非常巧妙的避免了上面的問題。但是真的是這樣的嗎?文章《雙重檢查鎖定及單例模式》中談到了非常多演變的雙重鎖機制帶來的問題,包括比較難以理解的指令重排序機制等。總之就是雙重檢查鎖機制仍然對導致錯誤問題而不是性能問題。
          于是繼續改造,某個牛人利用JVM的特性來解決上述問題,具體哪個牛人我忘記了,但是不是下面文章的作者。
          (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?




          上述代碼看起來解決了上面單例模式遇到的所有問題,而且實際上工作的很好,沒有什么問題。但是卻有一個致命的問題,如果第11行拋出了一個異常,也就是第一次構造函數失敗將導致永遠無法再次得到構建對象的機會。
          使用下面的代碼測試下。
          ?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?
          很不幸將得到以下輸出:
          ?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?

          很顯然我們想著第一次加載失敗第二次能夠加載成功,非常不幸,JVM一旦加載某個類失敗將認為此類的定義有問題,將來不再加載,這樣就導致我們沒有機會再加載。目前看起來沒有辦法避免此問題。如果要使用JVM的類加載特性就必須保證類加載一定正確,否則此問題將比并發和性能更嚴重。如果我們的類需要初始話那么就需要想其它辦法避免在構造函數中完成。看起來像是又回到了老地方,難道不是么?

          總之,結論是目前沒有一個十全十美的單例模式,而大多數情況下我們只需要滿足我們的需求就行,沒必有特意追求最“完美”解決方案。
          原文[http://www.imxylz.cn/p/177.html]

          posted on 2009-12-23 16:34 飛熊 閱讀(148) 評論(0)  編輯  收藏 所屬分類: JAVA基礎


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 米易县| 红桥区| 临江市| 天等县| 天津市| 许昌市| 云梦县| 科尔| 上饶市| 绥德县| 扶余县| 顺昌县| 九江市| 连州市| 四平市| 龙岩市| 巴彦县| 高青县| 泉州市| 抚远县| 石城县| 苏尼特右旗| 游戏| 苗栗县| 塘沽区| 吉隆县| 内江市| 洪湖市| 竹溪县| 海淀区| 囊谦县| 汉川市| 澳门| 聂荣县| 沙湾县| 开远市| 张北县| 佛山市| 石景山区| 湖南省| 阳新县|