彩虹天堂
          技術(shù)源于生活
          posts - 0,  comments - 2,  trackbacks - 0
          一種性能改進(jìn)的方法

          尋找一種性能改進(jìn)方法時(shí),你可能會(huì)選擇像下面這樣重寫getInstance()方法:
          Java代碼 復(fù)制代碼
          1. public static Singleton getInstance() {    
          2.    if(singleton == null) {    
          3.       synchronized(Singleton.class) {     
          4.          singleton = new Singleton();    
          5.       }    
          6.    }    
          7.    return singleton;    
          8. }   


          這個(gè)代碼片段只同步了關(guān)鍵的代碼,而不是同步整個(gè)方法。然而這段代碼卻不是線程安全的。考慮一下下面的假定:線程1進(jìn)入同步塊,并且在它給singleton成員變量賦值之前線程1被切換。接著另一個(gè)線程進(jìn)入if塊。第二個(gè)線程將等待直到第一個(gè)線程完成,并且仍然會(huì)得到兩個(gè)不同的單例類實(shí)例。有修復(fù)這個(gè)問題的方法嗎?請(qǐng)讀下去。

          雙重加鎖檢查

          初看上去,雙重加鎖檢查似乎是一種使懶漢式實(shí)例化為線程安全的技術(shù)。下面的代碼片段展示了這種技術(shù):
          Java代碼 復(fù)制代碼
          1. public static Singleton getInstance() {    
          2.   if(singleton == null) {    
          3.      synchronized(Singleton.class) {    
          4.        if(singleton == null) {    
          5.          singleton = new Singleton();    
          6.        }    
          7.     }    
          8.   }    
          9.   return singleton;    
          10. }   


          如果兩個(gè)線程同時(shí)訪問getInstance()方法會(huì)發(fā)生什么?想像一下線程1進(jìn)行同步塊馬上又被切換。接著,第二個(gè)線程進(jìn)入if 塊。當(dāng)線程1退出同步塊時(shí),線程2會(huì)重新檢查看是否singleton實(shí)例仍然為null。因?yàn)榫€程1設(shè)置了singleton成員變量,所以線程2的第二次檢查會(huì)失敗,第二個(gè)單例類實(shí)例也就不會(huì)被創(chuàng)建。似乎就是如此。
          不幸的是,雙重加鎖檢查不會(huì)保證正常工作,因?yàn)榫幾g器會(huì)在Singleton的構(gòu)造方法被調(diào)用之前隨意給singleton賦一個(gè)值。如果在singleton引用被賦值之后而被初始化之前線程1被切換,線程2就會(huì)被返回一個(gè)對(duì)未初始化的單例類實(shí)例的引用。

          一個(gè)改進(jìn)的線程安全的單例模式實(shí)現(xiàn)

          例7列出了一個(gè)簡(jiǎn)單、快速而又是線程安全的單例模式實(shí)現(xiàn):
          例7.一個(gè)簡(jiǎn)單的單例類
          Java代碼 復(fù)制代碼
          1. public class Singleton {    
          2.    public final static Singleton INSTANCE = new Singleton();    
          3.    private Singleton() {    
          4.          // Exists only to defeat instantiation.    
          5.       }    
          6. }   


          這段代碼是線程安全的是因?yàn)殪o態(tài)成員變量一定會(huì)在類被第一次訪問時(shí)被創(chuàng)建。你得到了一個(gè)自動(dòng)使用了懶漢式實(shí)例化的線程安全的實(shí)現(xiàn);你應(yīng)該這樣使用它:
          Java代碼 復(fù)制代碼
          1. Singleton singleton = Singleton.INSTANCE;    
          2. singleton.dothis();    
          3. singleton.dothat();    
          4. ...   


          當(dāng)然萬事并不完美,前面的Singleton只是一個(gè)折衷的方案;如果你使用那個(gè)實(shí)現(xiàn),你就無法改變它以便后來你可能想要允許多個(gè)單例類的實(shí)例。用一種更折哀的單例模式實(shí)現(xiàn)(通過一個(gè)getInstance()方法獲得實(shí)例)你可以改變這個(gè)方法以便返回一個(gè)唯一的實(shí)例或者是數(shù)百個(gè)實(shí)例中的一個(gè).你不能用一個(gè)公開且是靜態(tài)的(public static)成員變量這樣做.

          你可以安全的使用例7的單例模式實(shí)現(xiàn)或者是例1的帶一個(gè)同步的getInstance()方法的實(shí)現(xiàn).然而,我們必須要研究另一個(gè)問題:你必須在編譯期指定這個(gè)單例類,這樣就不是很靈活.一個(gè)單例類的注冊(cè)表會(huì)讓我們?cè)谶\(yùn)行期指定一個(gè)單例類.

          使用注冊(cè)表
          使用一個(gè)單例類注冊(cè)表可以:

          在運(yùn)行期指定單例類

          防止產(chǎn)生多個(gè)單例類子類的實(shí)例
          在例8的單例類中,保持了一個(gè)通過類名進(jìn)行注冊(cè)的單例類注冊(cè)表:
          例8 帶注冊(cè)表的單例類

          Java代碼 復(fù)制代碼
          1. import java.util.HashMap;    
          2. import org.apache.log4j.Logger;    
          3.      
          4. public class Singleton {    
          5.    private static HashMap map = new HashMap();    
          6.    private static Logger logger = Logger.getRootLogger();    
          7.      
          8.    protected Singleton() {    
          9.       // Exists only to thwart instantiation    
          10.    }    
          11.    public static synchronized Singleton getInstance(String classname) {    
          12.       if(classname == nullthrow new IllegalArgumentException("Illegal classname");    
          13.          Singleton singleton = (Singleton)map.get(classname);    
          14.      
          15.       if(singleton != null) {    
          16.          logger.info("got singleton from map: " + singleton);    
          17.          return singleton;    
          18.       }    
          19.       if(classname.equals("SingeltonSubclass_One"))    
          20.             singleton = new SingletonSubclass_One();             
          21.          else if(classname.equals("SingeltonSubclass_Two"))    
          22.             singleton = new SingletonSubclass_Two();    
          23.      
          24.       map.put(classname, singleton);    
          25.       logger.info("created singleton: " + singleton);    
          26.       return singleton;    
          27.    }    
          28.    // Assume functionality follows that's attractive to inherit    
          29. }   


          這段代碼的基類首先創(chuàng)建出子類的實(shí)例,然后把它們存儲(chǔ)在一個(gè)Map中。但是基類卻得付出很高的代價(jià)因?yàn)槟惚仨殲槊恳粋€(gè)子類替換它的getInstance()方法。幸運(yùn)的是我們可以使用反射處理這個(gè)問題。

          使用反射

          在例9的帶注冊(cè)表的單例類中,使用反射來實(shí)例化一個(gè)特殊的類的對(duì)象。與例8相對(duì)的是通過這種實(shí)現(xiàn),Singleton.getInstance()方法不需要在每個(gè)被實(shí)現(xiàn)的子類中重寫了。
          例9 使用反射實(shí)例化單例類
          Java代碼 復(fù)制代碼
          1. import java.util.HashMap;    
          2. import org.apache.log4j.Logger;    
          3.      
          4. public class Singleton {    
          5.    private static HashMap map = new HashMap();    
          6.    private static Logger logger = Logger.getRootLogger();    
          7.      
          8.    protected Singleton() {    
          9.       // Exists only to thwart instantiation    
          10.    }    
          11.    public static synchronized Singleton getInstance(String classname) {    
          12.       Singleton singleton = (Singleton)map.get(classname);    
          13.      
          14.       if(singleton != null) {    
          15.          logger.info("got singleton from map: " + singleton);    
          16.          return singleton;    
          17.       }    
          18.       try {    
          19.          singleton = (Singleton)Class.forName(classname).newInstance();    
          20.       }    
          21.       catch(ClassNotFoundException cnf) {    
          22.          logger.fatal("Couldn't find class " + classname);        
          23.       }    
          24.       catch(InstantiationException ie) {    
          25.          logger.fatal("Couldn't instantiate an object of type " + classname);        
          26.       }    
          27.       catch(IllegalAccessException ia) {    
          28.          logger.fatal("Couldn't access class " + classname);        
          29.       }    
          30.       map.put(classname, singleton);    
          31.       logger.info("created singleton: " + singleton);    
          32.      
          33.       return singleton;    
          34.    }    
          35. }   


          關(guān)于單例類的注冊(cè)表應(yīng)該說明的是:它們應(yīng)該被封裝在它們自己的類中以便最大限度的進(jìn)行復(fù)用。


          封裝注冊(cè)表

          例10列出了一個(gè)單例注冊(cè)表類。
          例10 一個(gè)SingletonRegistry類

          Java代碼 復(fù)制代碼
          1. import java.util.HashMap;    
          2. import org.apache.log4j.Logger;    
          3.      
          4. public class SingletonRegistry {    
          5.    public static SingletonRegistry REGISTRY = new SingletonRegistry();    
          6.      
          7.    private static HashMap map = new HashMap();    
          8.    private static Logger logger = Logger.getRootLogger();    
          9.      
          10.    protected SingletonRegistry() {    
          11.       // Exists to defeat instantiation    
          12.    }    
          13.    public static synchronized Object getInstance(String classname) {    
          14.       Object singleton = map.get(classname);    
          15.      
          16.       if(singleton != null) {    
          17.          return singleton;    
          18.       }    
          19.       try {    
          20.          singleton = Class.forName(classname).newInstance();    
          21.          logger.info("created singleton: " + singleton);    
          22.       }    
          23.       catch(ClassNotFoundException cnf) {    
          24.          logger.fatal("Couldn't find class " + classname);        
          25.       }    
          26.       catch(InstantiationException ie) {    
          27.          logger.fatal("Couldn't instantiate an object of type " +     
          28.                        classname);        
          29.       }    
          30.       catch(IllegalAccessException ia) {    
          31.          logger.fatal("Couldn't access class " + classname);        
          32.       }    
          33.       map.put(classname, singleton);    
          34.       return singleton;    
          35.    }    
          36. }   


          注意我是把SingletonRegistry類作為一個(gè)單例模式實(shí)現(xiàn)的。我也通用化了這個(gè)注冊(cè)表以便它能存儲(chǔ)和取回任何類型的對(duì)象。例11顯示了的Singleton類使用了這個(gè)注冊(cè)表。
          例11 使用了一個(gè)封裝的注冊(cè)表的Singleton類

          Java代碼 復(fù)制代碼
          1. import java.util.HashMap;    
          2. import org.apache.log4j.Logger;    
          3.      
          4. public class Singleton {    
          5.      
          6.    protected Singleton() {    
          7.       // Exists only to thwart instantiation.    
          8.    }    
          9.    public static Singleton getInstance() {    
          10.       return (Singleton)SingletonRegistry.REGISTRY.getInstance(classname);    
          11.    }    
          12. }   


          上面的Singleton類使用那個(gè)注冊(cè)表的唯一實(shí)例通過類名取得單例對(duì)象。
          現(xiàn)在我們已經(jīng)知道如何實(shí)現(xiàn)線程安全的單例類和如何使用一個(gè)注冊(cè)表去在運(yùn)行期指定單例類名,接著讓我們考查一下如何安排類載入器和處理序列化。
          posted on 2008-05-05 22:11 bcterry 閱讀(169) 評(píng)論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           

          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          留言簿

          文章檔案

          搜索

          •  

          最新評(píng)論

          主站蜘蛛池模板: 漳浦县| 梨树县| 稷山县| 同仁县| 平塘县| 砚山县| 汉川市| 泰顺县| 巴彦淖尔市| 华蓥市| 商城县| 营口市| 白玉县| 游戏| 陵川县| 娄烦县| 郓城县| 安仁县| 梅州市| 海南省| 宁化县| 霞浦县| 黑河市| 内乡县| 怀宁县| 亳州市| 吉林省| 霍城县| 禄丰县| 嘉荫县| 积石山| 平邑县| 凤城市| 茂名市| 双江| 上犹县| 兴宁市| 靖边县| 肃北| 木兰县| 滕州市|