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);
}