1. 程式人生 > >Junit測試多執行緒時遇到的問題

Junit測試多執行緒時遇到的問題

問題的產生
這個問題是我在做支付寶自動對賬功能時發現的,因為支付寶對賬介面下載的對賬單是zip壓縮檔案形式返回的,所以要實現自動對賬功能需要在我呼叫支付寶對賬介面下載完zip檔案之前先啟動一個執行緒去監控我用於存放zip檔案的資料夾,當這個資料夾有zip檔案生成時,自動觸發“解壓”方法來解壓zip檔案。方法寫好之後我用junit框架去測試,發現一直都沒能成功解壓zip檔案,我通過輸出日誌發現,是有調到“解壓”方法的,但是在“解壓”方法執行完成之前程式就已經終止了,後來我用main函式的方式代替了junit來進行測試,發現程式能自動解壓,由此得出:junit不支援多執行緒測試(這種說法不完全正確,因為網上好像有大神實現用junit測試多執行緒的,具體怎樣實現我還沒有深入研究,只確定原生的junit是不支援多執行緒測試的)


由於我自己寫的支付寶自動對賬功能涉及的程式碼較多,邏輯相對複雜,不太適合用於講解這個問題,所以我在網上找了另外一個例子供大家參考:例子來自https://www.cnblogs.com/yanphet/p/5774291.html

/**
 * @Title: TestDoWork.java
 * @Describe:
 * @author: Mr.Yanphet
 * @Email: [email protected]
 * @date: 2016年8月15日 下午5:50:03
 * @version: 1.0
 */
public class TestDoWork {

    class DoWork implements Runnable {

        @Override
public void run() { for (int i = 0; i < 10000; i++) { long milliSecond = System.currentTimeMillis(); System.out.println("i=" + i + ",milliSecond=" + milliSecond);// 輸出迴圈次數和當前的系統時間 } } } @Test public void test() { DoWork dw = new
DoWork(); Thread t = new Thread(dw); t.start(); } }

輸出結果

i=751,milliSecond=1471257586416
i=752,milliSecond=1471257586416
i=753,milliSecond=1471257586416
i=754,milliSecond=1471257586416
i=755,milliSecond=1471257586416
i=756,milliSecond=1471257586416
i=757,milliSecond=1471257586416
i=758,milliSecond=1471257586416

從結果可以看到,迴圈到了759次後就沒再輸出了,說明子執行緒還沒結束任務,整個程式就被強迫結束了。
既然知道了現象,那麼為什麼會出現這樣的現象呢,貼出部分Junit4 TestRunner原始碼就知道了

public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;

public static void main(String args[]) {
    TestRunner aTestRunner = new TestRunner();
    try {
        TestResult r = aTestRunner.start(args);
        if (!r.wasSuccessful())
            System.exit(FAILURE_EXIT);
        System.exit(SUCCESS_EXIT);
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.exit(EXCEPTION_EXIT);
    } 
}

再貼上TestResult部分原始碼,以供參考

protected  List<TestFailure>    fFailures
protected  List<TestFailure>    fErrors

public synchronized boolean wasSuccessful() {
    return failureCount() == 0 && errorCount() == 0;
}

public synchronized int errorCount() {
    return fErrors.size();
}

public synchronized int failureCount() {
    return fFailures.size();
}

在TestRunner中可以看出,如果是單執行緒,當測試主執行緒執行結束後,不管子執行緒是否結束,都會回撥TestResult的wasSuccessful方法,
然後判斷結果是成功還是失敗,最後呼叫相應的System.exit()方法。大家都知道這個方法是用來結束當前正在執行中的java虛擬機器,jvm都自身難保了,所以子執行緒也就對不住你咧…
解決辦法:
1 簡單粗暴地讓主執行緒休眠一段時間,然後讓子執行緒能夠執行結束。但是這個方法的弊端是,你不知道子執行緒的執行時間,所以需要看臉=_=
  Thread.sleep();
2 使用CountDownLatch工具類,讓主執行緒阻塞,直到子執行緒執行結束或者阻塞超時,這個方法要比第一個方法好點。
  countDownLatch.await(5, TimeUnit.MINUTES);
至於還有其他方法,筆者看到很多大神自己寫的Junit支援多執行緒,有興趣的讀者自行度娘…