遇到的问题:这两天写项目需求遇见一个大坑,代码两下写完了,测试卡了我整整一天,报错信息显示各种连接异常(oss异常或者sql异常),导致我大部分时间都陷入查找oss为什么连接不上的问题,耗费了大量时间。后来发现是单元测试时执行了异步任务的问题!把异步任务取消后,程序立马跑通了,搞了我一整天的问题终于落在帷幕,避免以后再次卡住,在此记录junit单元测试在执行多线程情况下的问题和结论,并对比junit单元测试和主程序Main方法之多线程执行。
一. 先放结论
切记:junit不支持多线程!!!在junit单元测试中,当创建了新线程后,单元测试并不会等待主线程下启动的新线程是否执行结束,只要主线程结束完成,单元测试就会关闭,导致主线程中启动的新线程不能顺利执行完!
- 对于junit单元测试,当
@Test注释
的单元测试方法执行时,实际上junit时将该方法作为参数传给了junit.textui.TestRunner
类的main
函数,并通过main
函数进行执行(源代码如下)。
package junit.textui;
public class TestRunner extends BaseTestRunner {
//...
public static void main(String args[]) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
if (!r.wasSuccessful()) {
System.exit(FAILURE_EXIT); // FAILURE_EXIT = 1
}
System.exit(SUCCESS_EXIT); // SUCCESS_EXIT = 0
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(EXCEPTION_EXIT); // EXCEPTION_EXIT = 2
}
}
}
- 当
@Test
注释的单元测试方法执行结束,TestRunner
类的main
函数将会调用System.exit(0)
。此时如果该单元测试方法中还有其他子线程在运行,TestRunner
类的main
函数也会调用System.exit(0)
。 System.exit()
是系统调用,用于通知系统立即结束jvm的运行。即使jvm中有线程在运行,jvm也会停止。System.exit(0)
:表示单元测试执行成功,系统正常退出。System.exit(1)
:表示单元测试执行失败,系统异常退出。System.exit(2)
:表示系统异常退出。
二. junit执行多线程示例
- Maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- 线程类
public class NumThread extends Thread {
public NumThread(String name){
super(name);
}
@Override
public void run(){
int sum = 0;
for(int i=1;i<=100;i++){
sum+=i;
System.out.println(getName()+"|"+i+"|"+sum);
}
System.out.println("线程运行完成1");
try {
Thread.sleep(5000);
}catch (Exception e){
System.out.println("catch Exception");
}
System.out.println("线程运行完成2");
}
}
- junit测试类
import org.junit.Test;
public class JunitTest {
@Test
public void test1(){
NumThread nt1 = new NumThread("nt1");
nt1.start();
System.out.println("test启动完成");
}
}
- 运行结果(每一次运行结果不同)
test启动完成
nt1|1|1
nt1|2|3
...
nt1|63|2016
nt1|64|2080
- 结论:在线程类中,循环打印100个数字,但是在junit测试类中只打印了一部分。因此当test1()主程序执行完成,不会等待子线程运行完毕,而是直接调用
System.exit(0)
关闭jvm。
三. Main主程序的执行示例
- 主程序
public class Main {
public static void main(String[] args) {
NumThread nt1 = new NumThread("nt1");
nt1.start();
System.out.println("Main方法执行完成");
}
}
- 测试结果
Main方法执行完成
nt1|1|1
nt1|2|3
nt1|3|6
nt1|4|10
....
nt1|99|4950
nt1|100|5050
线程运行完成1
线程运行完成2
- 可见,Main函数可以将数字全部打印出来。因此,Main函数会等待子线程执行完毕后,再关闭JVM。