Junit单元测试多线程方法不执行深度解析

一、背景

本来想借助redis实现一个自增序列,并模拟一下多线程场景下生成的序列是否会出错,代码如下:

		@Test
    public void test2(){
        int count = 10;
        for (int i = 0; i < count; i++) {
            Runnable target = () -> {
                Jedis jedis = jedisPool.getResource();
                Long num = jedis.incr("test");
                System.out.println("Thread: " + Thread.currentThread().getName() + " || num=" + num);

                jedis.close();
            };

            Thread thread = new Thread(target, "Thread" + i);
            thread.start();
        }

        System.out.println("END");
    }

哪想输出的结果并不如预期,Thread.run()中的方法并未执行,整个方法就结束了

image-20210218114221713

为什么会出现这种情况?

二、解决方法

测试方法:

   @Test
    public void test4() {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(100);
                System.out.println("子线程结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        System.out.println("main方法结束");
    }
  //输出:main方法结束

核心问题在于:在子线程结束执行前并未将主线程阻塞,主线程结束后,jvm直接退出

2.1 使用main函数

public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(100);
                System.out.println("子线程结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        System.out.println("main方法结束");
    }

2.2 使用countDownLatch

   @Test
    public void test5() throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(100);
                System.out.println("子线程结束");
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        System.out.println("main方法结束");

        countDownLatch.await();
    }

//main方法结束
//子线程结束

2.3 使用Thread.join

   @Test
    public void test6() throws Exception{
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(100);
                System.out.println("子线程结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        System.out.println("main方法结束");

        thread.join();
    }
    
  //main方法结束
  //子线程结束

当然还有其他方法,殊途同归,核心都是保证主线程退出前子线程先结束运行

三、原理

为什么会出现这样的现象,按照我们对守护/非守护线程的理解

非守护线程:我们平常创建的普通线程。

守护线程:用来服务于非守护线程(用户线程);不需要上层逻辑介入(JVM垃圾回收器)

new Thread()创建的默认是非守护线程,daemon默认为false,如果要改为守护线程,可以手动在start之前置为true

当main方法结束且只有守护线程存在时,jvm退出

按照上面的例子可以看出,@Test的执行原理和main方法有出入,下面我们采用调用栈大法看看Junit单元测试究竟是怎么执行的

image-20210218120223345

奇怪的是最下面框选的4行调用栈,双击发现没办法进入源码,正常来说是可以点进去的。

通过包名看出应该是idea的jar包,看包名com.intellij.rt.junit是很重要的线索

既然idea可以加载到这个包,这个jar应该在我们的类路径里,显然应该在idea的安装目录中

进入到idea的安装目录,到lib中没找到,到plugins找找,进入junit/lib

image-20210218120735671

发现这几个很可疑

使用反编译大法,借助jd-gui(下载地址http://java-decompiler.github.io/)反编译一下看看

从调用栈可以看出最先的入口为com.intellij.rt.junit包中的JunitStarter

image-20210218121100933

@Test执行

test方法运行在主线程中,外层函数执行完test等操作后执行System.exit来退出虚拟机,这个时候thread可能还没执行完,就被销毁了。

Main方法

启动完thread后,主线程结束,但是只有还有普通线程(非守护线程),虚拟机就不会主动退出,而我们也没有写代码让虚拟机退出,因此虚拟机需等待thread运行完毕才退出。因此打印出了子线程结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值