1、引言
有個需求要求將對一個接口進行併發測試,查看是否符合需求,由於習慣使用Junit進行測試,所以就寫了以下操作
@Test
public void testsend(){
final AtomicLong l = new AtomicLong(0);
long begin = System.currentTimeMillis();
ExecutorService pool = Executors.newFixedThreadPool(100);
for (int j = 0;j<100;j++ ){
Runnable t = new Runnable() {
public void run() {
for(int i = 0; i < 10000;i++){
MQProductHelper.send("ABILITY_BILL", "hello");
System.out.println(l.incrementAndGet());
}
}
};
pool.execute(t);
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
然後發現主線程立即執行完畢,然後其他線程的耗時操作還沒執行一會就全部終止了,一開始一臉懵逼的看着還以爲是自己代碼寫錯了不能併發操作,但是發現每次子線程還是有執行一會,只是任務沒結束線程就被殺掉了,接下來嘗試在main方法中進行測試,然後發現接口又可以正常測試,那麼是不是Junit不支持進行多線程單元測試呢,查找了下原因
2、原因
查看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虛擬機,所以子線程就全部GG了
3、解決方案
1、 主線程休眠
這個方案比較粗暴,而且無法計算運行時間,完全靠自己推測大概需要多長時間運行完子線程,然後讓主線程休眠一段時間
public void testsend(){
final AtomicLong l = new AtomicLong(0);
long begin = System.currentTimeMillis();
ExecutorService pool = Executors.newFixedThreadPool(100);
for (int j = 0;j<100;j++ ){
Runnable t = new Runnable() {
public void run() {
for(int i = 0; i < 10000;i++){
MQProductHelper.send("ABILITY_BILL", "hello");
System.out.println(l.incrementAndGet());
}
}
};
pool.execute(t);
}
long end = System.currentTimeMillis();
try {
Thread.sleep(120000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(end - begin);
}
2.使用CountDownLatch工具類,讓主線程阻塞,直到子線程運行結束或者阻塞超時
@Test
public void testsend(){
CountDownLatch latch=new CountDownLatch(100);
final AtomicLong l = new AtomicLong(0);
long begin = System.currentTimeMillis();
ExecutorService pool = Executors.newFixedThreadPool(100);
for (int j = 0;j<100;j++ ){
Runnable t = new Runnable() {
public void run() {
for(int i = 0; i < 10000;i++){
MQProductHelper.send("ABILITY_BILL", "hello");
System.out.println(l.incrementAndGet());
}
}
};
pool.execute(t);
}
long end = System.currentTimeMillis();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(end - begin);
}