为什么junit单元测试会关闭非守护进程

2022-03-28 20:14:44 浏览数 (1)

在网上有不少人发现了这个问题,却没有说明这个问题背后的故事,今天笔者就从源头把这个问题解释下,先把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

0 人点赞