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支援多執行緒,有興趣的讀者自行度娘…