在网上有不少人发现了这个问题,却没有说明这个问题背后的故事,今天笔者就从源头把这个问题解释下,先把demo程序中用到的两个类代码贴出来:
消费者类
代码语言:javascript复制public class Consumer implements Runnable{
private final TransferQueue<String> queue;
public Consumer(TransferQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
System.out.println("Consumer " Thread.currentThread().getName() queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生产者类
代码语言:javascript复制public class Producer implements Runnable {
private final TransferQueue<String> queue;
public Producer(TransferQueue<String> queue) {
this.queue = queue;
}
private String produce() {
return "随机数字" (new Random().nextInt(100));
}
@Override
public void run() {
try {
while (true) {
if (queue.hasWaitingConsumer()) {
queue.transfer(produce());
}
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e){
}
}
}
接下来看下下面两段测试代码:
1、在main方法中测试,测试结果是非守护进程不会退出,消费者线程都能拿到数据,消费者线程拿到数据后就直接退出了,最后就会剩下生产者线程独自运行。
代码语言:javascript复制 /**
**一段生产者消费者demo
***/
public static void main(String[] args) {
TransferQueue<String> queue = new LinkedTransferQueue<String>();
Thread producer = new Thread(new Producer(queue));
//producer.setDaemon(true);
producer.start();
for (int i = 0; i < 10; i ) {
Thread consumer = new Thread(new Consumer(queue));
//consumer.setDaemon(true);
consumer.start();
try {
//防止进程提前结束
Thread.sleep(1000L);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
2、在junit单元测试中测试上述代码:
代码语言:javascript复制 @Test
public void transferQueue() {
println(Thread.currentThread().isDaemon());
TransferQueue<String> queue = new LinkedTransferQueue<String>();
Thread producer = new Thread(new Producer(queue));
producer.setDaemon(true);
println(producer.isDaemon());
//producer.setDaemon(true);
producer.start();
for (int i = 0; i < 10; i ) {
Thread consumer = new Thread(new Consumer(queue));
//consumer.setDaemon(true);
consumer.start();
try {
//防止进程提前结束
Thread.sleep(1000L);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
println("结束");
}
上述代码执行结果是:大约5个消费者线程拿到数据后,整个程序退出,从 println(Thread.currentThread().isDaemon());输出我们可以看到运行单元测试的线程是个非守护线程,那么由该线程创建的生产者以及消费者线程也会继承父线程特性,成为非守护线程,我们都知道,非守护线程没结束前,JVM是不会退出的,那么这里为什么会出现这种情况呢,下面看下核心类org.eclipse.jdt.internal.junit.runner.RemoteTestRunner,这是用eclipse执行junit单元测试时用到的类,下面贴出单元测试执行的原理:
代码语言:javascript复制 public static void main(String[] args) {
try {
RemoteTestRunner testRunServer= new RemoteTestRunner();
testRunServer.init(args);
testRunServer.run();
} catch (Throwable e) {
e.printStackTrace(); // don't allow System.exit(0) to swallow exceptions
} finally {
// fix for 14434
System.exit(0);
}
}
从上面的代码可以看出当所有的单元测试都执行完毕后会调用System.exit(0)退出,这也就是为什么单元测试中非守护线程也会退出的原因!
参考文章:
1、https://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/tree/org.eclipse.jdt.junit.runtime/src/org/eclipse/jdt/internal/junit/runner/RemoteTestRunner.java?id=d0add1708b812c80384d88ef4eb52493b862187d