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

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

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

    隨筆 - 71  文章 - 15  trackbacks - 0
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    因?yàn)榭诳?,上帝?chuàng)造了水;
    因?yàn)楹诎担系蹌?chuàng)造了火;
    因?yàn)槲倚枰笥?,所以上帝讓你?lái)到我身邊
    Click for Shaanxi xi'an, Shaanxi Forecast
    ╱◥█◣
      |田|田|
    ╬╬╬╬╬╬╬╬╬╬╬
    If only I have such a house!
    〖總在爬山 所以艱辛〗
    Email:myesjoy@yahoo.com.cn
    NickName:yesjoy
    MSN:myesjoy@hotmail.com
    QQ:150230516

    〖總在尋夢(mèng) 所以苦痛〗

    常用鏈接

    留言簿(3)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    Hibernate在線

    Java友情

    Java認(rèn)證

    linux經(jīng)典

    OA系統(tǒng)

    Spring在線

    Structs在線

    專家專欄

    企業(yè)信息化

    大型設(shè)備共享系統(tǒng)

    工作流

    工作流產(chǎn)品

    網(wǎng)上購(gòu)書

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    測(cè)試單例模式


    接下來(lái),我使用與log4j相對(duì)應(yīng)的JUnit來(lái)測(cè)試單例類,它會(huì)貫穿在這篇文章余下的部分。如果你對(duì)JUnit或log4j不很熟悉,請(qǐng)參考相關(guān)資源。

    例2是一個(gè)用JUnit測(cè)試?yán)?的單例模式的案例:
    例2.一個(gè)單例模式的案例
    1. import org.apache.log4j.Logger;
    2. import junit.framework.Assert;
    3. import junit.framework.TestCase;
    4. public class SingletonTest extends TestCase {
    5.    private ClassicSingleton sone = null, stwo = null;
    6.    private static Logger logger = Logger.getRootLogger();
    7.    public SingletonTest(String name) {
    8.       super(name);
    9.    }
    10.    public void setUp() {
    11.       logger.info("getting singleton...");
    12.       sone = ClassicSingleton.getInstance();
    13.       logger.info("...got singleton: " + sone);
    14.       logger.info("getting singleton...");
    15.       stwo = ClassicSingleton.getInstance();
    16.       logger.info("...got singleton: " + stwo);
    17.    }
    18.    public void testUnique() {
    19.       logger.info("checking singletons for equality");
    20.       Assert.assertEquals(true, sone == stwo);
    21.    }
    22. }

    例2兩次調(diào)用ClassicSingleton.getInstance(),并且把返回的引用存儲(chǔ)在成員變量中。方法testUnique()會(huì)檢查這些引用看它們是否相同。例3是這個(gè)測(cè)試案例的輸出:
    例3.是這個(gè)測(cè)試案例的輸出
    1. Buildfile: build.xml
    2. init:
    3.      [echo] Build 20030414 (14-04-2003 03:08)
    4. compile:
    5. run-test-text:
    6.      [java] .INFO main: [b]getting singleton...[/b]
    7.      [java] INFO main: [b]created singleton:[/b] Singleton@e86f41
    8.      [java] INFO main: ...got singleton: Singleton@e86f41
    9.      [java] INFO main: [b]getting singleton...[/b]
    10.      [java] INFO main: ...got singleton: Singleton@e86f41
    11.      [java] INFO main: checking singletons for equality
    12.      [java] Time: 0.032
    13.      [java] OK (1 test)

    正如前面的清單所示,例2的簡(jiǎn)單測(cè)試順利通過(guò)----通過(guò)ClassicSingleton.getInstance()獲得的兩個(gè)單例類的引用確實(shí)相同;然而,你要知道這些引用是在單線程中得到的。下面的部分著重于用多線程測(cè)試單例類。

    多線程因素的考慮


    在例1中的ClassicSingleton.getInstance()方法由于下面的代碼而不是線程安全的:
    1. 1: if(instance == null) {
    2. 2:    instance = new Singleton();
    3. 3: }

    如果一個(gè)線程在第二行的賦值語(yǔ)句發(fā)生之前切換,那么成員變量instance仍然是null,然后另一個(gè)線程可能接下來(lái)進(jìn)入到if塊中。在這種情況下,兩個(gè)不同的單例類實(shí)例就被創(chuàng)建。不幸的是這種假定很少發(fā)生,這樣這種假定也很難在測(cè)試期間出現(xiàn)(譯注:在這可能是作者對(duì)很少出現(xiàn)這種情況而導(dǎo)致無(wú)法測(cè)試從而使人們放松警惕而感到嘆惜)。為了演示這個(gè)線程輪換,我得重新實(shí)現(xiàn)例1中的那個(gè)類。例4就是修訂后的單例類:
    例4.人為安排的方式
    1. import org.apache.log4j.Logger;
    2. public class Singleton {
    3.   private static Singleton singleton = null;
    4.   private static Logger logger = Logger.getRootLogger();
    5.   private static boolean firstThread = true;
    6.   protected Singleton() {
    7.     // Exists only to defeat instantiation.
    8.   }
    9.   public static Singleton getInstance() {
    10.      if(singleton == null) {
    11.         simulateRandomActivity();
    12.         singleton = new Singleton();
    13.      }
    14.      logger.info("created singleton: " + singleton);
    15.      return singleton;
    16.   }
    17.   private static void simulateRandomActivity() {
    18.      try {
    19.         if(firstThread) {
    20.            firstThread = false;
    21.            logger.info("sleeping...");
    22.            // This nap should give the second thread enough time
    23.            // to get by the first thread.
    24.              Thread.currentThread().sleep(50);
    25.        }
    26.      }
    27.      catch(InterruptedException ex) {
    28.         logger.warn("Sleep interrupted");
    29.      }
    30.   }
    31. }

    除了在這個(gè)清單中的單例類強(qiáng)制使用了一個(gè)多線程錯(cuò)誤處理,例4類似于例1中的單例類。在getInstance()方法第一次被調(diào)用時(shí),調(diào)用這個(gè)方法的線程會(huì)休眠50毫秒以便另外的線程也有時(shí)間調(diào)用getInstance()并創(chuàng)建一個(gè)新的單例類實(shí)例。當(dāng)休眠的線程覺(jué)醒時(shí),它也會(huì)創(chuàng)建一個(gè)新的單例類實(shí)例,這樣我們就有兩個(gè)單例類實(shí)例。盡管例4是人為如此的,但它卻模擬了第一個(gè)線程調(diào)用了getInstance()并在沒(méi)有完成時(shí)被切換的真實(shí)情形。
    例5測(cè)試了例4的單例類:
    例5.失敗的測(cè)試
    1. import org.apache.log4j.Logger;
    2. import junit.framework.Assert;
    3. import junit.framework.TestCase;
    4. public class SingletonTest extends TestCase {
    5.    private static Logger logger = Logger.getRootLogger();
    6.    private static Singleton singleton = null;
    7.    public SingletonTest(String name) {
    8.       super(name);
    9.    }
    10.    public void setUp() {
    11.       singleton = null;
    12.    }
    13.    public void testUnique() throws InterruptedException {
    14.       // Both threads call Singleton.getInstance().
    15.       Thread threadOne = new Thread(new SingletonTestRunnable()),
    16.              threadTwo = new Thread(new SingletonTestRunnable());
    17.       threadOne.start();
    18.       threadTwo.start();
    19.       threadOne.join();
    20.       threadTwo.join();
    21.    }
    22.    private static class SingletonTestRunnable implements Runnable {
    23.       public void run() {
    24.          // Get a reference to the singleton.
    25.          Singleton s = Singleton.getInstance();
    26.          // Protect singleton member variable from
    27.          // multithreaded access.
    28.          synchronized(SingletonTest.class) {
    29.             if(singleton == null// If local reference is null...
    30.                singleton = s;     // ...set it to the singleton
    31.          }
    32.          // Local reference must be equal to the one and
    33.          // only instance of Singleton; otherwise, we have two
    34.                   // Singleton instances.
    35.          Assert.assertEquals(true, s == singleton);
    36.       }
    37.    }
    38. }

    例5的測(cè)試案例創(chuàng)建兩個(gè)線程,然后各自啟動(dòng),等待完成。這個(gè)案例保持了一個(gè)對(duì)單例類的靜態(tài)引用,每個(gè)線程都會(huì)調(diào)用Singleton.getInstance()。如果這個(gè)靜態(tài)成員變量沒(méi)有被設(shè)置,那么第一個(gè)線程就會(huì)將它設(shè)為通過(guò)調(diào)用getInstance()而得到的引用,然后這個(gè)靜態(tài)變量會(huì)與一個(gè)局部變量比較是否相等。
    在這個(gè)測(cè)試案例運(yùn)行時(shí)會(huì)發(fā)生一系列的事情:第一個(gè)線程調(diào)用getInstance(),進(jìn)入if塊,然后休眠;接著,第二個(gè)線程也調(diào)用getInstance()并且創(chuàng)建了一個(gè)單例類的實(shí)例。第二個(gè)線程會(huì)設(shè)置這個(gè)靜態(tài)成員變量為它所創(chuàng)建的引用。第二個(gè)線程檢查這個(gè)靜態(tài)成員變量與一個(gè)局部備份的相等性。然后測(cè)試通過(guò)。當(dāng)?shù)谝粋€(gè)線程覺(jué)醒時(shí),它也會(huì)創(chuàng)建一個(gè)單例類的實(shí)例,并且它不會(huì)設(shè)置那個(gè)靜態(tài)成員變量(因?yàn)榈诙€(gè)線程已經(jīng)設(shè)置過(guò)了),所以那個(gè)靜態(tài)變量與那個(gè)局部變量脫離同步,相等性測(cè)試即告失敗。例6列出了例5的輸出:
    例6.例5的輸出
    1. Buildfile: build.xml
    2. init:
    3.      [echo] Build 20030414 (14-04-2003 03:06)
    4. compile:
    5. run-test-text:
    6. INFO Thread-1: sleeping...
    7. INFO Thread-2: created singleton: Singleton@7e5cbd
    8. INFO Thread-1: created singleton: Singleton@704ebb
    9. junit.framework.AssertionFailedError: expected: but was:
    10.    at junit.framework.Assert.fail(Assert.java:47)
    11.    at junit.framework.Assert.failNotEquals(Assert.java:282)
    12.    at junit.framework.Assert.assertEquals(Assert.java:64)
    13.    at junit.framework.Assert.assertEquals(Assert.java:149)
    14.    at junit.framework.Assert.assertEquals(Assert.java:155)
    15.    at SingletonTest$SingletonTestRunnable.run(Unknown Source)
    16.    at java.lang.Thread.run(Thread.java:554)
    17.      [java] .
    18.      [java] Time: 0.577
    19.      [java] OK (1 test)

    到現(xiàn)在為止我們已經(jīng)知道例4不是線程安全的,那就讓我們看看如何修正它。

    同步


    要使例4的單例類為線程安全的很容易----只要像下面一個(gè)同步化getInstance()方法:
    1. public synchronized static Singleton getInstance() {
    2.    if(singleton == null) {
    3.       simulateRandomActivity();
    4.       singleton = new Singleton();
    5.    }
    6.    logger.info("created singleton: " + singleton);
    7.    return singleton;
    8. }

    在同步化getInstance()方法后,我們就可以得到例5的測(cè)試案例返回的下面的結(jié)果:
    1. Buildfile: build.xml
    2. init:
    3.      [echo] Build 20030414 (14-04-2003 03:15)
    4. compile:
    5.     [javac] Compiling 2 source files
    6. run-test-text:
    7. INFO Thread-1: sleeping...
    8. INFO Thread-1: created singleton: Singleton@ef577d
    9. INFO Thread-2: created singleton: Singleton@ef577d
    10.      [java] .
    11.      [java] Time: 0.513
    12.      [java] OK (1 test)

    這此,這個(gè)測(cè)試案例工作正常,并且多線程的煩惱也被解決;然而,機(jī)敏的讀者可能會(huì)認(rèn)識(shí)到getInstance()方法只需要在第一次被調(diào)用時(shí)同步。因?yàn)橥降男阅荛_(kāi)銷很昂貴(同步方法比非同步方法能降低到100次左右),或許我們可以引入一種性能改進(jìn)方法,它只同步單例類的getInstance()方法中的賦值語(yǔ)句。

    一種性能改進(jìn)的方法


    尋找一種性能改進(jìn)方法時(shí),你可能會(huì)選擇像下面這樣重寫getInstance()方法:
    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è)問(wèn)題的方法嗎?請(qǐng)讀下去。

    雙重加鎖檢查


    初看上去,雙重加鎖檢查似乎是一種使懶漢式實(shí)例化為線程安全的技術(shù)。下面的代碼片段展示了這種技術(shù):
    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í)訪問(wèn)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)單的單例類
    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ì)在類被第一次訪問(wèn)時(shí)被創(chuàng)建。你得到了一個(gè)自動(dòng)使用了懶漢式實(shí)例化的線程安全的實(shí)現(xiàn);你應(yīng)該這樣使用它:
    1.       Singleton singleton = Singleton.INSTANCE;
    2.       singleton.dothis();
    3.       singleton.dothat();
    4.       ...

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

    你可以安全的使用例7的單例模式實(shí)現(xiàn)或者是例1的帶一個(gè)同步的getInstance()方法的實(shí)現(xiàn).然而,我們必須要研究另一個(gè)問(wèn)題:你必須在編譯期指定這個(gè)單例類,這樣就不是很靈活.一個(gè)單例類的注冊(cè)表會(huì)讓我們?cè)谶\(yùn)行期指定一個(gè)單例類.
    posted on 2006-02-14 15:33 ★yesjoy★ 閱讀(225) 評(píng)論(0)  編輯  收藏 所屬分類: 設(shè)計(jì)模式
    主站蜘蛛池模板: heyzo亚洲精品日韩| **aaaaa毛片免费| 特级毛片在线大全免费播放| 亚洲国产精品美女久久久久| 亚洲六月丁香婷婷综合| 亚洲一卡2卡4卡5卡6卡在线99| 亚洲精品在线免费观看| 亚洲精品美女久久久久9999| 亚洲的天堂av无码| 久久亚洲AV无码精品色午夜| 亚洲精品视频在线观看视频| 亚洲一欧洲中文字幕在线| 亚洲jjzzjjzz在线播放| 亚洲欧洲精品成人久久曰| 亚洲.国产.欧美一区二区三区| 爱爱帝国亚洲一区二区三区| 免费国产在线精品一区| jzzjzz免费观看大片免费| 中文字幕无线码免费人妻| 一个人免费日韩不卡视频| 亚洲免费网站在线观看| 最近免费中文字幕大全视频 | 亚洲欧洲日韩国产一区二区三区| 亚洲国产成人久久精品app| 亚洲一区二区三区写真 | 亚洲AV中文无码乱人伦下载| 亚洲国产一区在线| 亚洲精品中文字幕无乱码麻豆| 亚洲Av永久无码精品一区二区| 免费人妻精品一区二区三区| 你好老叔电影观看免费| 中国人xxxxx69免费视频| 天天干在线免费视频| 国产精品亚洲w码日韩中文| 久久精品国产亚洲av麻豆| 亚洲一级片在线观看| 国产亚洲精品91| 波多野结衣免费一区视频 | 免费观看在线禁片| 免费可以看黄的视频s色| 国产一区二区三区免费看|