Junit單元測試無法正常啟用多執行緒
阿新 • • 發佈:2021-12-18
測試Redis setnx實現分散式鎖,使用Junit單元測試,但是每次執行5、6秒程式就退了,然後報了redis相關的一堆錯,一直以為是redis的原因,各種查,然後發現,淦,就是因為Junit單元測試不支援多執行緒
。
原博文:Junit單元測試多執行緒的問題
部分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);
測試程式碼
@Autowired
private RedisService redisService;
@Test
void test1() {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Boolean setnx = redisService.setnx("TASK_CHECK_SCHEME_STATE_LOCK", System.currentTimeMillis(), 1, TimeUnit.SECONDS);
System.out.println("===========thread1 lock =======" + setnx);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Boolean setnx = redisService.setnx("TASK_CHECK_SCHEME_STATE_LOCK", System.currentTimeMillis(), 1, TimeUnit.SECONDS);
System.out.println("==============thread2 lock ============ "+setnx);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread1.start();
thread2.start();
// 暫停主執行緒
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await(10, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
使用countDownLatch.await(10, TimeUnit.HOURS);
使主執行緒不立刻結束,即可實現在Junit測試單元實現多執行緒。
===========thread1 lock =======true
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
...
至於還有其他方法,筆者看到很多大神自己寫的Junit支援多執行緒,有興趣的讀者自行度娘...