Junit无法正常测试多线程问题原因分析与解决

发表于:2018-6-04 10:52  作者:Since   来源:Since1986

字体: | 上一篇 | 下一篇 |我要投稿 | 推荐标签: 软件测试工具 JUnit 白盒测试工具

  今天在 Junit 中尝试调试多线程业务,结果发现,testXX 方法中早早就退出了虚拟机,启动的子线程全都压根没有执行,也就是 Junit 中无法正常的测试多线程。
  分析
  一开始,我以为是在主线程中出现了异常,导致了虚拟机的退出,但是做了一个小实验分析了一下,发现不是那么回事。经过试验,我发现,无论是 Exception RuntimeException 甚至 Error 都不能造成虚拟机的退出,子线程都能被执行到,只有 System.exit(n) 才能直接退掉虚拟机,忽略子线程的执行。
package com.github.since1986.learn.java;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LearnThread {
public static void main(String[] args) throws Exception {
int subThreadCount = 9;
ExecutorService executorService = Executors.newFixedThreadPool(subThreadCount);
for (int i = 0; i < subThreadCount; i++) {
executorService.submit(() -> {
System.out.printf("线程 [%s] 运行\n", Thread.currentThread().getName());
});
}
//throw new Exception();
//throw new RuntimeException();
//throw new Error();
System.exit(0);
}
}
  输出
  Process finished with exit code 0
  因此,大致可以猜测,Junit 中在什么地方调用了 System.exit() 所以造成了子线程无法执行,Google 了一下, 有这篇文章 也研究了这个问题,果然在 Junit 中有一个类 org.junit.runner.JUnitCore 包含了 System.exit()
/**
* Run the tests contained in the classes named in the <code>args</code>.
* If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
* Write feedback while tests are running and write
* stack traces for all failed tests after the tests all complete.
*
* @param args names of classes in which to find tests to run
*/
public static void main(String... args) {
Result result = new JUnitCore().runMain(new RealSystem(), args);
System.exit(result.wasSuccessful() ? 0 : 1);
}
  我想在这个类上打上断点试一下,走一遍,结果惊奇的发现,断点并没有走到这个,然后看了看 debug 窗中线程 main 发现有这么一个类 com.intellij.rt.execution.junit.JUnitStarter 最后 main() 原来是在这了类里的,这个类明显是 idea 内部的一个插件类。
  Google 了一下,实际上这个插件是开源的, 可以看到这个类的源代码 ,大致上看了一眼,里面也是调用了 System.exit() (不管怎样,最后 testXX 方法肯定会是通过 main() 来执行的,所以我们看 main() 就好了),所以,大只能印证个大概,是因为 Junit 当中使用了 System.exit() ,所以导致了子线程的执行因为虚拟机的退出而被忽略,所以无法正常测试多线程。
public static void main(String[] args) throws IOException {
Vector argList = new Vector();
for (int i = 0; i < args.length; i++) {
String arg = args[i];
argList.addElement(arg);
}
final ArrayList listeners = new ArrayList();
final String[] name = new String[1];
String agentName = processParameters(argList, listeners, name);
if (!JUNIT5_RUNNER_NAME.equals(agentName) && !canWorkWithJUnitVersion(System.err, agentName)) {
System.exit(-3);
}
if (!checkVersion(args, System.err)) {
System.exit(-3);
}
String[] array = new String[argList.size()];
argList.copyInto(array);
int exitCode = prepareStreamsAndStart(array, agentName, listeners, name[0]);
System.exit(exitCode);
}
  解决
  其实明白了大致的原因,解决起来也就简单了,让主线程等待子线程都执行完毕就好了,实现这一点有多种方式,可以用”倒计时门栓”或者”可循环使用屏障”。(实际上我中间请教了我的同事,他并没有分析这莫多,大致凭经验告诉我可以使用倒计时门栓,我当时试了确实管用)
  总结
  通过这次解决工作中出现的一个问题的契机,我对 Junit 以及多线程又加深了一些理解。总的来说,对于理论的理解要在实践中不断的磨练才能越来越深入。

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。

【有奖活动】填问卷 送70G测试大牛精品资料(干货+视频)!

评 论

论坛新帖

顶部 底部


建议使用IE 6.0以上浏览器,800×600以上分辨率,法律顾问:上海瀛东律师事务所 张楠律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2018, 沪ICP备05003035号
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪公网安备 31010102002173号

51Testing官方微信

51Testing官方微博

扫一扫 测试知识全知道