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

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

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

    JavaExplore

    一切像霧像雨又像風(fēng)
    posts - 19, comments - 45, trackbacks - 0, articles - 0

    【原創(chuàng)】單例模式陷阱

    Posted on 2006-08-26 11:26 JavaExplore 閱讀(4900) 評論(13)  編輯  收藏

    ???????? 今天去jdon,看了它的設(shè)計研究欄目,bang有幾篇評論單例模式的文章,聲稱“Singleton is evil”(見http://www.jdon.com/jive/article.jsp?forum=91&thread=17578),并且引用幾篇外文頁面佐證自己的觀點,其中有一篇文章更是說,單例不僅不是一種模式,而是一種反模式。
    ??????? 下面我談?wù)勎覍卫J降目捶āV鹨环治鰡卫J降南葳澹瑤椭蠹艺_使用單例模式。
    (1)?陷阱一:調(diào)用函數(shù)的性能瓶頸
    ??????? 在c++中,單例只有一種實現(xiàn)方式——LazySingleton, 實現(xiàn)如下(本文全部使用java代碼):

    public ? class ?LazySingleton {
    ????
    private ? static ?LazySingleton?m_instance = null ;
    ???? private?LazySingleton(){};
    ?????synchronized?public?static?LazySingleton?getInstance(){
    ????????
    if(m_instance==null)
    ????????????m_instance
    =new?LazySingleton();
    ????????
    return?m_instance;
    ????}

    }

    LazySingleton將對象的初始化推遲到調(diào)用的時候。并且為了防止多線程環(huán)境下產(chǎn)生多個實例,使用synchronized關(guān)鍵字保證函數(shù)getInstance調(diào)用的線程安全。synchronized關(guān)鍵字的存在保證了只會產(chǎn)生一個對象,但也成了多線程環(huán)境下的性能瓶頸。一個多線程的程序,到了這里卻要排隊等候成了一個單線程式的執(zhí)行流程,這在高并發(fā)環(huán)境下是不可容忍的。而c++中可以使用雙重檢查機(jī)制將這種性能問題僅僅限制在第一次構(gòu)造對象的時候,而java中不可以使用雙重檢查機(jī)制。
    ????????但是java可以實現(xiàn)EagerSingleton,實現(xiàn)如下:

    public ? class ?EagerSingleton {
    ????
    private ? static ?EagerSingleton?m_instance = new ?EagerSingleton();
    ???? private?EagerSingleton(){};
    ???? public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    }
    與LazySingleton相比,EagerSingleton將對象的初始化放到了類加載的時候。這樣就避免了synchronized關(guān)鍵字的性能瓶頸。
    (2)陷阱二:訪問互斥共享資源
    ?????????EagerSingleton中訪問互斥資源也要考慮線程安全問題。下面看一個例子:
    public?class?EagerSingleton{
    ????
    private?static?EagerSingleton?m_instance=new?EagerSingleton();
    ????
    private?HashMap?map=new?HashMap();
    ???
    private?EagerSingleton(){};
    ???
    public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    ???????
    public?void?refreshMap(Object key){
    ????????
    synchronized(map){
    ????????????
    if(!map.contains(key))
    ????????????????map.put(key,value);
    //value為此時的實時數(shù)據(jù)
    ????????}
    ?
    ????}

    }
    因為該類是單例,可能多線程并發(fā)訪問map,map非線程安全,需要加線程安全關(guān)鍵字,否則就掉入了訪問互斥資源的陷阱。
    (3)陷阱三:非法邏輯陷阱
    ??????? 這種情況一般是濫用單例模式造成的,下面考慮一種濫用單例的情況。下面的代碼的作用是getValueByName后,馬上printValue即完成操作流程。
    public?class?EagerSingleton{
    ????
    private?static?EagerSingleton?m_instance=new?EagerSingleton();
    ????
    private?String?value=null;
    ???
    private?EagerSingleton(){};
    ???
    public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    ????
    synchronized?public?void?getValueByName(String?name){
    ????????value
    =getByNameFromDateBase(name);
    ????????
    ????}

    ????
    public?viod?printValue(){
    ????????System.out.println(
    this.vaue);
    ????}

    }

    該類含有一私有屬性value,在多線程環(huán)境下不能保證value值的合理邏輯,一線程getValueByName后,馬上printValue,也有可能value的值已經(jīng)被其他線程修改。這種情況就屬于單例模式的濫用,該類根本不適合做成單例。
    ??????? 消除非法邏輯的陷阱,可以通過將該類重構(gòu)為純粹的行為類完成。重構(gòu)后的代碼如下:

    public?class?EagerSingleton{
    ????
    private?static?EagerSingleton?m_instance=new?EagerSingleton();
    ???
    private?EagerSingleton(){};
    ???
    public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    ????
    private?String?getValueByName(String?name){
    ????????
    return?getByNameFromDateBase(name);
    ????????
    ????}

    ????
    public?viod?printName(String?name){
    ????????String?value
    =getValueByName(String?name);
    ????????System.out.println(value);
    ????}

    }

    通過調(diào)用printName(String name)直接完成操作流程,將其中的私有屬性處理成過程式的參數(shù)傳遞,將該類修改成純粹的行為類。

    ??????? 含有私有屬性并且含有對它賦值操作的類并非都會調(diào)入該陷阱,構(gòu)造函數(shù)里進(jìn)行對私有屬性賦值不會引起非法邏輯,如下代碼

    public?class?EagerSingleton{
    ????
    private?static?EagerSingleton?m_instance=new?EagerSingleton();
    ????
    private?HashMap?map==new?HashMap();
    ????
    ??? private
    ?EagerSingleton(){
    ????????map.put(key,value);
    //value為此時的實時數(shù)據(jù)
    ????}

    ???
    public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    }

    構(gòu)造函數(shù)里不必要加線程安全關(guān)鍵字也可以保證線程安全,因為類加載器是線程安全的,EagerSingleton只會在類加載的時候?qū)嵗淮危@樣不會出現(xiàn)單例模式的線程不安全,也不會造成非法邏輯。
    (4)陷阱四:單例陷阱的傳遞
    ??????? 當(dāng)含有對象作為單例類的私有屬性時,陷阱不僅會出現(xiàn)在該類本身,還會傳遞到私有對象所在的類中。看如下代碼:

    public?class?EagerSingleton{
    ????
    private?static?EagerSingleton?m_instance=new?EagerSingleton();
    ????
    private?NewClass?newClass=nll;
    ???
    private?EagerSingleton(){
    ????????newClass
    =new?NewClass();
    ????}
    ;
    ???
    public?static?agerSingleton?getInstance(){
    ????????
    return?m_instance;
    ????}

    ???
    public?viod?printName(String?name){
    ????????String?value
    =newClass.operationByNameAndReturnValue(String?name);
    ????????System.out.println(value);
    ????}

    }
    乍一看,代碼中除了構(gòu)造函數(shù)對私有屬性進(jìn)行了初始化操作,其他地方?jīng)]有對私有屬性的賦值,不會引起非法邏輯陷阱。其實這個賦值操作可能隱含在newClass.operationByNameAndReturnValue(String name)操作,只有保證了NewClass的operationByNameAndReturnValue操作不會對它的私有屬性賦值操作,才能保證真正的合理邏輯。同樣,只有保證NewClass的operationByNameAndReturnValue操作沒有掉入訪問互斥資源陷阱,才能真正保證EagerSingleton沒有掉入該陷阱。
    ??????? 消除該陷阱的方法:(1)類方法的名稱要合理,比如純粹的行為方法名:interprete,excute,operation之類的方法中就不該含有對私有屬性直接或者間接的賦值操作,每個方法的責(zé)任要明確。(2)單例類中盡量不要含有非單例類的實例作為私有屬性(容器類除外),一定要有類的實例作為私有屬性的時候,重新審視這個作為私有屬性的類,是不是也應(yīng)該設(shè)計成單例類;或者保證對它的初始化賦值限制在構(gòu)造函數(shù)內(nèi)。

    Feedback

    # re: 單例模式陷阱  回復(fù)  更多評論   

    2006-08-27 10:25 by lover
    精辟!很有收獲

    # re: 單例模式陷阱  回復(fù)  更多評論   

    2006-08-27 10:28 by lover
    你搞的不錯!

    # re: 單例模式陷阱  回復(fù)  更多評論   

    2006-08-27 12:26 by cw
    大牛!受教了!

    # re: 單例模式陷阱  回復(fù)  更多評論   

    2006-08-28 16:27 by kalala
    咬住

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-09-13 09:00 by 陳琪
    第一段
    public class LazySingleton {
    private static LazySingleton m_instance = null ;
    private LazySingleton(){};
    synchronized public static LazySingleton getInstance(){
    if(m_instance!=null)
    m_instance=new LazySingleton();
    return m_instance;
    }
    這個代碼是不是寫錯了?
    if(m_instance!=null)
    m_instance=new LazySingleton();
    是不是應(yīng)該為
    if(m_instance==null)
    m_instance=new LazySingleton();

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-09-13 09:13 by 陳琪
    類加載器是線程安全的,受教了

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-09-14 19:44 by JavaExplore
    @陳琪
    謝謝提醒 已經(jīng)改正

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-11-21 22:30 by fish[匿名]
    頂下..

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-11-30 16:59 by 路過
    調(diào)用函數(shù)的性能瓶頸<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    鄙人以為這種瓶頸不屬于模式的問題,多線程的方式里,都有這樣的問題。要訪問共享資源,就會有瓶頸,乃多線程的屬性,與模式無關(guān)。

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2006-11-30 23:16 by JavaExplore [匿名]
    @路過
    你推翻了c++下經(jīng)典的雙重檢測機(jī)制

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2007-08-10 17:03 by dreamstone
    其實可以用基于classloader的機(jī)制來實現(xiàn)。大部分情況下是可用的,不過如果基于多個classloader的程序則不行了。

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2007-12-13 11:27 by 深秋小雨
    @JavaExplore [匿名]
    雙重成例檢測在Java中不成立,不能使用。閻博士說得很清楚了。

    # re: 【原創(chuàng)】單例模式陷阱  回復(fù)  更多評論   

    2007-12-13 11:41 by 深秋小雨
    喔,我搞錯情況了……不好意思

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: a色毛片免费视频| 91成人免费在线视频| 亚洲高清在线mv| 在线播放高清国语自产拍免费| 美女被爆羞羞网站在免费观看| 亚洲精品乱码久久久久久 | 免费无码又爽又刺激高潮的视频| 国产成人亚洲精品播放器下载 | 亚洲黄片毛片在线观看| 久久久久久AV无码免费网站| 亚洲国产成a人v在线观看 | 亚洲AV无码专区国产乱码不卡| 亚洲综合精品香蕉久久网| 歪歪漫画在线观看官网免费阅读| 九九九精品视频免费| 亚洲影视一区二区| 亚洲永久精品ww47| 免费视频淫片aa毛片| 午夜免费福利视频| 免费无毒a网站在线观看| 亚洲一区精品视频在线| 亚洲人成图片小说网站| 日本免费一区二区三区最新| 嫩草在线视频www免费观看| 精品亚洲av无码一区二区柚蜜| 亚洲图片一区二区| 亚洲精品成人片在线观看| 免费a级毛片高清视频不卡 | 91在线视频免费观看| www亚洲精品久久久乳| 7777久久亚洲中文字幕蜜桃| 亚洲福利在线播放| 暖暖日本免费在线视频| 67pao强力打造高清免费| 在线免费观看伊人三级电影| 国产亚洲男人的天堂在线观看| 亚洲最新中文字幕| 久久夜色精品国产亚洲AV动态图| av无码东京热亚洲男人的天堂| 成人看的午夜免费毛片| 国产成人免费网站|