携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
下面这段代码,创建了两个线程,t1睡眠3s再输出,t2等待t1执行完毕再输出
@Test
public void _join() {
Thread t1 = new Thread(()->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t1");
});
Thread t2 = new Thread(()->{
try {
t1.join();
System.out.println("t2");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t2.start();
t1.start();
}
预期输出是不是应该是这样?
t1
t2
Process finished with exit code 0
但其实输出结果是这样
Process finished with exit code 0
我们来分析一下,用户线程随着主线程结束只有一种情况
- 用户线程被偷偷设置为了守护线程,我们来打印一下
...
t2.start();
t1.start();
System.out.println("t1 t2你们是Daemon吗?"+t1.isDaemon()+' '+t2.isDaemon());
t1 t2你们是Daemon吗?false false
Process finished with exit code 0
答案是没有被设置为用户线程
那又是什么原因呢?会不会是主线程结束太快了还没来得及启动子线程?
我们来给主线程加一个Sleep()试试
...
t2.start();
t1.start();
Thread.sleep(1000);
结果是...
Process finished with exit code 0
这会我已经抓狂了,说好用户线程不会随主线程结束而结束呢??为什么别人的demo能跑,我的不能呢?这时候就使用排除法,首先排除我长得帅这个原因,那就是你了,Junit4.@Test,
于是把测试方法换成main方法,程序就正常执行了
为什么呢?我们给方法打上断点,开始DEBUG
直接将调用栈拉到最后,发现JUnitCore类中有一个run方法(再往下的方法进不去,不在classpath内,是插件),往上滑发现了Junit的main方法,如下图
Result result = (new JunitCore()).runMain(new RealSystem(),args);
System.exit(result.wasSuccessful()?0:1);
分析上面Main函数的代码得知,无论程序执行的结果是什么,Junit都会直接退出JVM,所以这就造成了我们子线程还在睡觉,家被拆了的事实
若我们需要通过@Test注解使用多线程,则需要使用能阻塞主线程的方法,因为不阻塞主线程,Junit就直接把你JVM干没了
比如可以这样做
- 使用
join()方法阻塞主线程
...
t2.start();
t1.start();
t1.join();
t2.join();
- 使用
CountDownLatch等等