<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    憨厚生

    ----Java's Slave----
    ***Java's Host***

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      165 隨筆 :: 17 文章 :: 90 評論 :: 0 Trackbacks
    轉 http://blog.csdn.net/haoel/archive/2009/03/26/4028232.aspx

    深入淺出單實例Singleton設計模式

    陳皓

    前序

    單實例Singleton設計模式可能是被討論和使用的最廣泛的一個設計模式了,這可能也是面試中問得最多的一個設計模式了。這個設計模式主要目的 是想在整個系統中只能出現一個類的實例。這樣做當然是有必然的,比如你的軟件的全局配置信息,或者是一個Factory,或是一個主控類,等等。你希望這 個類在整個系統中只能出現一個實例。當然,作為一個技術負責人的你,你當然有權利通過使用非技術的手段來達到你的目的。比如:你在團隊內部明文規 定,“XX類只能有一個全局實例,如果某人使用兩次以上,那么該人將被處于2000元的罰款!”(呵呵),你當然有權這么做。但是如果你的設計的是東西是 一個類庫,或是一個需要提供給用戶使用的API,恐怕你的這項規定將會失效。因為,你無權要求別人會那么做。所以,這就是為什么,我們希望通過使用技術的 手段來達成這樣一個目的的原因。

    本文會帶著你深入整個Singleton的世界,當然,我會放棄使用C++語言而改用Java語言,因為使用Java這個語言可能更容易讓我說明一些事情。


    Singleton的教學版本

    這里,我將直接給出一個Singleton的簡單實現,因為我相信你已經有這方面的一些基礎了。我們姑且把這具版本叫做1.0版

    1. // version 1.0   
    2. public class Singleton   
    3. {   
    4.     private static final Singleton singleton = null;   
    5.   
    6.     private Singleton()   
    7.     {   
    8.     }   
    9.     public static Singleton getInstance()   
    10.     {   
    11.         if (singleton== null)   
    12.         {   
    13.             singleton= new Singleton();   
    14.         }   
    15.         return singleton;   
    16.     }   
    17. }  

    在上面的實例中,我想說明下面幾個Singleton的特點:(下面這些東西可能是盡人皆知的,沒有什么新鮮的)

    1. 私有(private)的構造函數,表明這個類是不可能形成實例了。這主要是怕這個類會有多個實例。
    2. 即然這個類是不可能形成實例,那么,我們需要一個靜態的方式讓其形成實例:getInstance()。注意這個方法是在new自己,因為其可以訪問私有的構造函數,所以他是可以保證實例被創建出來的。
    3. 在getInstance()中,先做判斷是否已形成實例,如果已形成則直接返回,否則創建實例。
    4. 所形成的實例保存在自己類中的私有成員中。
    5. 我們取實例時,只需要使用Singleton.getInstance()就行了。

    當然,如果你覺得知道了上面這些事情后就學成了,那我給你當頭棒喝一下了,事情遠遠沒有那么簡單。

    Singleton的實際版本

    上面的這個程序存在比較嚴重的問題,因為是全局性的實例,所以,在多線程情況下,所有的全局共享的東西都會變得非常的危險,這個也一樣,在多線程情 況下,如果多個線程同時調用getInstance()的話,那么,可能會有多個進程同時通過 (singleton== null)的條件檢查,于是,多個實例就創建出來,并且很可能造成內存泄露問題。嗯,熟悉多線程的你一定會說——“我們需要線程互斥或同步”,沒錯,我們 需要這個事情,于是我們的Singleton升級成1.1版,如下所示:

    1. // version 1.1   
    2. public class Singleton   
    3. {   
    4.     private static final Singleton singleton = null;   
    5.   
    6.     private Singleton()   
    7.     {   
    8.     }   
    9.     public static Singleton getInstance()   
    10.     {   
    11.         if (singleton== null)   
    12.         {   
    13.             synchronized (Singleton.class) {   
    14.                 singleton= new Singleton();   
    15.             }   
    16.         }   
    17.         return singleton;   
    18.     }   
    19. }  

    嗯,使用了Java的synchronized方法,看起來不錯哦。應該沒有問題了吧?!錯!這還是有問題!為什么呢?前面已經說過,如果有多個線 程同時通過(singleton== null)的條件檢查(因為他們并行運行),雖然我們的synchronized方法會幫助我們同步所有的線程,讓我們并行線程變成串行的一個一個去 new,那不還是一樣的嗎?同樣會出現很多實例。嗯,確實如此!看來,還得把那個判斷(singleton== null)條件也同步起來。于是,我們的Singleton再次升級成1.2版本,如下所示:

     
    1. // version 1.2   
    2. public class Singleton   
    3. {   
    4.     private static final Singleton singleton = null;   
    5.   
    6.     private Singleton()   
    7.     {   
    8.     }   
    9.     public static Singleton getInstance()   
    10.     {   
    11.         synchronized (Singleton.class)   
    12.         {   
    13.             if (singleton== null)   
    14.             {   
    15.                 singleton= new Singleton();   
    16.             }   
    17.         }   
    18.         return singleton;   
    19.     }   
    20. }  

    不錯不錯,看似很不錯了。在多線程下應該沒有什么問題了,不是嗎?的確是這樣的,1.2版的Singleton在多線程下的確沒有問題了,因為我們 同步了所有的線程。只不過嘛……,什么?!還不行?!是的,還是有點小問題,我們本來只是想讓new這個操作并行就可以了,現在,只要是進入 getInstance()的線程都得同步啊,注意,創建對象的動作只有一次,后面的動作全是讀取那個成員變量,這些讀取的動作不需要線程同步啊。這樣的 作法感覺非常極端啊,為了一個初始化的創建動作,居然讓我們達上了所有的讀操作,嚴重影響后續的性能啊!

    還得改!嗯,看來,在線程同步前還得加一個(singleton== null)的條件判斷,如果對象已經創建了,那么就不需要線程的同步了。OK,下面是1.3版的Singleton。

    1. // version 1.3   
    2. public class Singleton   
    3. {   
    4.     private static final Singleton singleton = null;   
    5.   
    6.     private Singleton()   
    7.     {   
    8.     }   
    9.     public static Singleton getInstance()   
    10.     {   
    11.         if (singleton== null)   
    12.         {   
    13.             synchronized (Singleton.class)   
    14.             {   
    15.                 if (singleton== null)   
    16.                 {   
    17.                     singleton= new Singleton();   
    18.                 }   
    19.             }   
    20.         }   
    21.         return singleton;   
    22.     }   
    23. }  

    感覺代碼開始變得有點羅嗦和復雜了,不過,這可能是最不錯的一個版本了,這個版本又叫“雙重檢查”Double-Check。下面是說明:

    1. 第一個條件是說,如果實例創建了,那就不需要同步了,直接返回就好了。
    2. 不然,我們就開始同步線程。
    3. 第二個條件是說,如果被同步的線程中,有一個線程創建了對象,那么別的線程就不用再創建了。

    相當不錯啊,干得非常漂亮!請大家為我們的1.3版起立鼓掌!

    Singleton的其它問題

    怎么?還有問題?!當然還有,請記住下面這條規則——“無論你的代碼寫得有多好,其只能在特定的范圍內工作,超出這個范圍就要出Bug了”,這是“陳式第一定理”,呵呵。你能想一想還有什么情況會讓這個我們上面的代碼出問題嗎?

    在C++下,我不是很好舉例,但是在Java的環境下,嘿嘿,還是讓我們來看看下面的一些反例和一些別的事情的討論(當然,有些反例可能屬于鉆牛角尖,可能有點學院派,不過也不排除其實際可能性,就算是提個醒吧):

    其一、Class Loader。不知道你對Java的Class Loader熟悉嗎?“類裝載器”?!C++可沒有這個東西啊。這是Java動態性的核心。顧名思義,類裝載器是用來把類(class)裝載進JVM的。 JVM規范定義了兩種類型的類裝載器:啟動內裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)。 在一個JVM中可能存在多個ClassLoader,每個ClassLoader擁有自己的NameSpace。一個ClassLoader只能擁有一個 class對象類型的實例,但是不同的ClassLoader可能擁有相同的class對象實例,這時可能產生致命的問題。如ClassLoaderA, 裝載了類A的類型實例A1,而ClassLoaderB,也裝載了類A的對象實例A2。邏輯上講A1=A2,但是由于A1和A2來自于不同的 ClassLoader,它們實際上是完全不同的,如果A中定義了一個靜態變量c,則c在不同的ClassLoader中的值是不同的。

    于是,如果咱們的Singleton 1.3版本如果面對著多個Class Loader會怎么樣?呵呵,多個實例同樣會被多個Class Loader創建出來,當然,這個有點牽強,不過他確實存在。難道我們還要整出個1.4版嗎?可是,我們怎么可能在我的Singleton類中操作 Class Loader啊?是的,你根本不可能。在這種情況下,你能做的只有是——“保證多個Class Loader不會裝載同一個Singleton”。

    其二、序例化。如果我們的這個Singleton類是一個關于我們程序配置信息的類。我們需要它有序列化的功能,那么,當反序列化的時候,我們將無法控制別人不多次反序列化。不過,我們可以利用一下Serializable接口的readResolve()方法,比如:

    1. public class Singleton implements Serializable   
    2. {   
    3.     ......   
    4.     ......   
    5.     protected Object readResolve()   
    6.     {   
    7.         return getInstance();   
    8.     }   
    9. }  

    其三、多個Java虛擬機。如果我們的程序運行在多個Java的虛擬機中。什么?多個虛擬機?這是一種什么樣的情況啊。嗯,這種情況是有點極端,不過還是可能出現,比如EJB或RMI之流的東西。要在這種環境下避免多實例,看來只能通過良好的設計或非技術來解決了。

    其四,volatile變量。關于volatile這個關鍵字所聲明的變量可以被看作是一種 “程度較輕的同步synchronized”;與 synchronized 塊相比,volatile 變量所需的編碼較少,并且運行時開銷也較少,但是它所能實現的功能也僅是synchronized的一部分。當然,如前面所述,我們需要的 Singleton只是在創建的時候線程同步,而后面的讀取則不需要同步。所以,volatile變量并不能幫助我們即能解決問題,又有好的性能。而且, 這種變量只能在JDK 1.5+版后才能使用。

    其五、關于繼承。是的,繼承于Singleton后的子類也有可能造成多實例的問題。不過,因為我們早把Singleton的構造函數聲明成了私有的,所以也就杜絕了繼承這種事情。

    其六,關于代碼重用。也話我們的系統中有很多個類需要用到這個模式,如果我們在每一個類都中有這樣的代碼,那么 就顯得有點傻了。那么,我們是否可以使用一種方法,把這具模式抽象出去?在C++下這是很容易的,因為有模板和友元,還支持棧上分配內存,所以比較容易一 些(程序如下所示),Java下可能比較復雜一些,聰明的你知道怎么做嗎?

    1. template<CLASS T> class Singleton   
    2. {   
    3.     public:   
    4.         static T& Instance()   
    5.         {   
    6.             static T theSingleInstance; //假設T有一個protected默認構造函數   
    7.             return theSingleInstance;   
    8.         }   
    9. };   
    10.   
    11. class OnlyOne : public Singleton<ONLYONE>   
    12. {   
    13.     friend class Singleton<ONLYONE>;   
    14.     int example_data;   
    15.   
    16.     public:   
    17.         int GetExampleData() const {return example_data;}   
    18.     protected:   
    19.         OnlyOne(): example_data(42) {}   // 默認構造函數   
    20.         OnlyOne(OnlyOne&) {}   
    21. };   
    22.   
    23. int main( )   
    24. {   
    25.     cout << OnlyOne::Instance().GetExampleData()<< endl;   
    26.     return 0;   
    27. }  

     (轉載時請注明作者和出處。未經許可,請勿用于商業用途)

    (全文完)



    posted on 2009-07-06 14:25 二胡 閱讀(230) 評論(0)  編輯  收藏 所屬分類: Java設計相關
    主站蜘蛛池模板: 免费少妇a级毛片人成网| 国语成本人片免费av无码 | 成人无遮挡裸免费视频在线观看 | 四虎成人精品永久免费AV| 亚洲国产一二三精品无码| 在线免费播放一级毛片 | 亚洲国产精品一区二区久| 很黄很黄的网站免费的| tom影院亚洲国产一区二区| 黄色成人网站免费无码av| 亚洲av无码成人影院一区| 亚洲JIZZJIZZ中国少妇中文| 免费精品视频在线| 亚洲精品~无码抽插| 亚洲免费视频播放| 亚洲欧美日韩久久精品| 亚洲成a人片在线观看日本麻豆 | 久久国产亚洲观看| 亚洲精品在线免费观看| 亚洲а∨天堂久久精品9966| 久久久久亚洲AV片无码| 亚洲最大免费视频网| 国产亚洲精品成人AA片| 免费在线观看黄网| 国产成年无码久久久免费| 亚洲电影在线免费观看| 国产又黄又爽又刺激的免费网址 | 香蕉视频免费在线| 亚洲一区二区女搞男| 亚洲人成免费电影| 精品成人一区二区三区免费视频 | 久久久久亚洲精品天堂久久久久久| a在线观看免费视频| 亚洲电影唐人社一区二区| 国产男女猛烈无遮档免费视频网站| eeuss影院ss奇兵免费com| 久久亚洲美女精品国产精品| 免费无码又爽又刺激高潮| 中文字幕无码免费久久9一区9| 亚洲视频欧洲视频| 亚洲精品尤物yw在线影院|