遇到背景

本周分享一个小问题,在看TIJ(Java编程思想)中多线程机制一节时,把书中的示例使用Junit来测试。发
现与书中使用main方法来测试的结果是不一样的。

问题描述

为了描述问题,先引用书中的例子:定义一个简单地任务,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LiftOff implements Runnable {
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;

public LiftOff() {}

public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "liftoff!") + "),";
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.println(status());
Thread.yield();
}
}
}

使用Junit测试代码如下:

1
2
3
4
5
6
7
8
9
@Test
public void testMoreBasicThread() throws InterruptedException {
for (int i = 0; i < 5; ++i) {
Thread t = new Thread(new LiftOff());
//t.join();
t.start();
}
System.out.println("Waiting for LiftOff");
}

使用main测试的结果与Junit测试的结果相比对,就会发现两者不同。

main函数测试的结果 Waiting for LiftOff#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(liftoff!),
Junit测试的结果 Waiting for LiftOff#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),

为什么使用Junit输出的结果会比使用main方法直接测试的要少。

分析与解决

一般情况下,只有当所有的守护线程完结之后JVM才会结束。为此,你可以在执行任务的线程上调用
t.setDaemon(false)来防止JVM在任务未执行完成之前就结束生命了。但是Junit会在主线程结束时调用
System.exit()。具体的见JunitCore.java中代码,下面是与此问题相关的代码1。解决方法就是在
调用t.start()之前调用t.jion(),让Junit线程等待任务的完成2

1
2
3
public static Result runClasses(Class<?>... classes) {
return runClasses(defaultComputer(), classes);
}

参考资料


  1. 1.https://github.com/junit-team/junit4/blob/master/src/main/java/org/junit/runner/JUnitCore.java#L48
  2. 2.https://stackoverflow.com/questions/16616590/thread-behaving-strangely-in-junit