Junit使用注意事项:
Junit单元测试不支持多线程。。
当新建线程,Junit单元测试在主线程运行结束后就关闭,不会等子线程运行结束。main方法则不会存在这样的问题。
问题代码演示:
方法1:使用Junit做多线程测试,发现程序并不会卡在读的线程中,理论上到读线程获取到锁后,由于没有外界条件操作,是卡死在read的方法中,但是如下代码达不到响应的效果。
原因在于Junit单元测试在主线程运行结束后就关闭了,不会等子线程运行结束。
public class ThreadTest1 {
//定义一个成员变量初始值为0
private int number = 0;
// 定义一把锁,当某一线程获取到此对象,其他线程只能处于等待状态直至锁释放
private final Object lock = new Object();
boolean complete = false;
private void read() {
synchronized (lock) {
while (!complete) {
System.out.println("还未完成写...");
}
System.out.println("number = " + number);
}
}
// 对方法进行加锁
private void write(int change) {
synchronized (lock) {
number += change;
}
}
@Test//首次尝试当number未到完成写,读线程等待。
public void test3() throws InterruptedException {
//开启线程加10000次
new Thread(() -> {
for (int i = 0; i < 100; i++) {
write(1);
System.out.println("写入 " + number);
}
System.out.println("加1操作完成");
complete = true;
}).start();
//开启线程减10000次
new Thread(() -> {
for (int i = 0; i < 100; i++) {
read();
}
System.out.println("读取结束");
}).start();
Thread.sleep(1000);
}
}
执行结果:
程序正常执行结束。
方法2:使用main方法来演示如上场景,当read获取到锁后程序会一直卡在此处。
package com.weidd.best.multiThread;
import org.junit.Test;
public class ThreadTest2 {
//定义一个成员变量初始值为0
private static int number = 0;
// 定义一把锁,当某一线程获取到此对象,其他线程只能处于等待状态直至锁释放
private static final Object lock = new Object();
static boolean complete = false;
private static void read() {
synchronized (lock) {
while (!complete) {
System.out.println("还未完成写...");
}
System.out.println("number = " + number);
}
}
// 对方法进行加锁
private static void write(int change) {
synchronized (lock) {
number += change;
}
}
//首次尝试当number未到完成写,读线程等待。
public static void main(String[] args) throws InterruptedException {
System.out.println("");
//开启线程加10000次
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
write(1);
System.out.println("写入 " + number);
}
System.out.println("加1操作完成");
complete = true;
}).start();
//开启线程减100次
new Thread(() -> {
for (int i = 0; i < 100; i++) {
read();
}
System.out.println("读取结束");
}).start();
Thread.sleep(1000);
}
}
问题分析:
对于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)
:表示系统异常退出。