今天看《高并发》书,写了一个多线程的类,发现测试方法输出的结果并不对;
同样的代码,经过main方法后输出的结果却是正常的;经过研究后发现单元测试@Test并不支持多线程测试;以下为分析过程:
private static ExecutorService ex = Executors.newFixedThreadPool(5);
public static class myTask implements Runnable{
@Override
public void run() {
long s0 = System.currentTimeMillis();
System.out.println(s0 + ":" + Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Logger.getLogger(getClass()).debug("线程异常");
}
}
}
/**
* @discription 这个测试类只能输出5条线程数据,原因为在@Test的junit_jar包中TestRunner方法有个特性:当测试类的主线程完成后回直接关闭jvm(System.exit),不会等待子线程运行完;所以主线程会在第一批线程处理完成后直接退出
* @author liu_l
* @created 2017年5月12日 上午10:21:26
* @throws InterruptedException
*/
@Test
public void test() throws InterruptedException{
myTask task = new myTask();
for(int i=0; i<10; i++){
ex.execute(task);
}
}
输出结果为:
time:1494558540525____ThreadId:10
time:1494558540525____ThreadId:11
time:1494558540525____ThreadId:12
time:1494558540526____ThreadId:13
time:1494558540526____ThreadId:14
这个输出明显是有问题的,讲道理在线程池这5条线程运行完过一秒会有相同的5条线程继续执行剩下来的5条线程;正常输出结果:
time:1494558751747____ThreadId:9
time:1494558751747____ThreadId:11
time:1494558751747____ThreadId:10
time:1494558751747____ThreadId:12
time:1494558751747____ThreadId:13
time:1494558752747____ThreadId:10
time:1494558752747____ThreadId:11
time:1494558752747____ThreadId:9
time:1494558752747____ThreadId:13
time:1494558752747____ThreadId:12
那么问题来了,why?这时候我想起来看下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类:
/**
* Returns whether the entire test was successful or not.
*/
public synchronized boolean wasSuccessful() {
return failureCount() == 0 && errorCount() == 0;
}
在这里我们明显可以看到:当aTestRunner调用start方法后不会去等待子线程执行完毕在关闭主线程,而是直接调用TestResult.wasSuccessful()方法,而这个方法始终返回的是false,所以主线程接下来就会执行System.exit,这个放回会结束当前运行的jvm虚拟机,所以使用junit测试多线程方法的结果异常就正常了;
(ps:想要正常输出的话可以让主线程不要结束,等待子线程全部运行结束后在结束主线程,输出结果就会正常,下面会junit测试多线程正常输出的方法:`@Test
public void test1() throws InterruptedException{
myTask task = new myTask();
for(int i=0; i<10; i++){
ex.execute(task);
}
Thread.sleep(100000);
}`)