You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit测试子线程未执行finally与Java main方法的差异原因

问题场景

示例1:JUnit测试方法无法打印finally块输出

如下为一个junit测试方法,执行该方法不会打印finally块的输出内容。

@Test
public void junit_Thread() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("run");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("finally");
            }
        }
    }).start();
    System.out.println("end");
}

示例2:普通main方法可正常打印finally块输出

如下为一个典型的Java main方法,执行该方法会等待3秒后打印finally块的输出内容。

public class MainThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("run");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("finally");
                }
            }
        }).start();
        System.out.println("main thread exit");
    }
}

问题

为何示例1的JUnit测试方法无法像示例2的main方法一样,等待子线程执行完成并输出finally块的内容,二者的JVM退出判定、线程生命周期管理逻辑存在什么差异?


原因解答

首先明确JVM的两类退出规则:

  1. 自然退出:进程内所有非守护(用户)线程全部执行完毕,JVM自动关闭
  2. 强制退出:代码主动调用System.exit()方法,JVM会立刻终止所有运行中的线程、直接关闭进程,和当前存活线程的类型、执行状态无关。

普通main方法的运行逻辑

直接执行main方法启动Java程序时:

  • main主线程默认是非守护线程,手动new Thread()创建的子线程默认也继承非守护属性,不属于会被JVM自动回收的低优先级守护线程
  • 当main方法执行到最后一行打印main thread exit后,main线程运行结束,但此时启动的子线程还处于3秒sleep的存活状态,属于未完成的非守护线程
  • JVM会保持运行状态,直到子线程sleep结束、执行完finally块的finally打印、线程正常销毁后,才会自然退出,因此能看到预期的输出。

JUnit测试方法的运行逻辑

执行@Test标注的测试方法时,执行逻辑和直接跑main方法有本质区别:

  • JUnit有独立的测试运行器,会在自己的调度线程中调用测试方法,当测试方法内的所有代码执行完毕(也就是打印完end返回时),JUnit就判定当前测试用例执行完成
  • JUnit不会主动等待测试方法中手动启动的子线程运行结束,测试方法返回后,JUnit的收尾流程会主动调用System.exit(0)强制终止JVM,避免测试进程挂住
  • 此时子线程的3秒等待还没结束,直接被JVM强制终止,没有机会执行到finally块的打印逻辑,自然看不到对应输出。

补充说明:不存在JUnit自动把子线程设为守护线程的情况,手动创建的子线程默认依然是非守护状态,只是System.exit()的强制终止逻辑不会给线程留执行收尾代码的时间。


内容的提问来源于stack exchange,提问作者tangcheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:54:22