排坑-junit单元测试和Main方法之多线程

遇到的问题:

这两天写项目需求遇见一个大坑,代码两下写完了,测试卡了我整整一天,多线程获取zk分布式锁,没等多线程跑起来,程序就结束了。后来发现是单元测试时执行了异步任务的问题!把异步任务取消后,用main 启动程序立马跑通了,搞了我一整天的问题终于落在帷幕,避免以后再次卡住,在此记录junit单元测试在执行多线程情况下的问题和结论,并对比junit单元测试和主程序Main方法之多线程执行。

一. 先放结论

切记:junit不支持多线程!!!在junit单元测试中,当创建了新线程后,单元测试并不会等待主线程下启动的新线程是否执行结束,只要主线程结束完成,单元测试就会关闭,导致主线程中启动的新线程不能顺利执行完!

对于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):表示系统异常退出。

二. junit执行多线程示例

Maven依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
       </dependency>
</dependencies>

线程类

public class NumThread extends Thread {
    
    public NumThread(String name){
        super(name);
    }

    @Override
    public void run(){
        int sum = 0;
        for(int i=1;i<=100;i++){
            sum+=i;
            System.out.println(getName()+"|"+i+"|"+sum);
        }
        System.out.println("线程运行完成1");
        
        try {
            Thread.sleep(5000);
        }catch (Exception e){
            System.out.println("catch Exception");
        }
        
        System.out.println("线程运行完成2");
    }
}

junit测试类

import org.junit.Test;

public class JunitTest {

    @Test
    public void test1(){
        NumThread nt1 = new NumThread("nt1");
        nt1.start();
        System.out.println("test启动完成");
    }
}

运行结果(每一次运行结果不同)

test启动完成
nt1|1|1
nt1|2|3
...
nt1|63|2016
nt1|64|2080

结论:在线程类中,循环打印100个数字,但是在junit测试类中只打印了一部分。因此当test1()主程序执行完成,不会等待子线程运行完毕,而是直接调用System.exit(0)关闭jvm。

三. Main主程序的执行示例

主程序

public class Main {
    public static void main(String[] args) {
        NumThread nt1 = new NumThread("nt1");
        nt1.start();

        System.out.println("Main方法执行完成");
    }
}

测试结果

Main方法执行完成
nt1|1|1
nt1|2|3
nt1|3|6
nt1|4|10
....
nt1|99|4950
nt1|100|5050
线程运行完成1
线程运行完成2

可见,Main函数可以将数字全部打印出来。因此,Main函数会等待子线程执行完毕后,再关闭JVM。

你可能感兴趣的:(java基础,单元测试,java,压力测试)