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

为何存在未完成CompletionStage时主线程不终止?

问题解析与解决方案

嘿,这个问题我太熟了!咱们来搞清楚为啥主线程明明执行完代码了,却还得等50秒才终止~

问题根源

你代码里用了Executors.newCachedThreadPool(),这个线程池默认创建的是非守护线程。JVM的运行规则是:只要还有非守护线程在运行,JVM就不会主动退出——哪怕主线程已经执行完所有逻辑。

你的子线程里调用了Thread.sleep(50000),这个线程是非守护状态,所以JVM必须等它跑完这50秒,才会终止整个程序,这就是你看到主线程输出I am dying ...后却不退出的原因。

解决方案

这里有两种常用的解决思路,你可以根据自己的需求选择:

1. 将线程池的线程设置为守护线程

自定义线程工厂,把线程池里的线程标记为守护线程。这样主线程结束后,JVM会直接终止所有守护线程,不用等它们完成任务。修改你的calculate()方法如下:

private CompletionStage<Integer> calculate() {
    CompletionStage<Integer> completionStage = new CompletableFuture<>();
    // 自定义线程工厂,创建守护线程
    ExecutorService executor = Executors.newCachedThreadPool(runnable -> {
        Thread thread = new Thread(runnable);
        thread.setDaemon(true); // 关键:设置为守护线程
        return thread;
    });
    executor.submit(() -> {
        System.out.println("I am in the thread: " + Thread.currentThread().getName());
        try {
            Thread.sleep(50000);
            ((CompletableFuture<Integer>) completionStage).complete(3);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    });
    // 可选:如果不需要再提交任务,关闭线程池
    executor.shutdown();
    return completionStage;
}

2. 手动控制线程池的生命周期

如果你需要让子线程完成任务,但又不想让它阻止JVM退出,可以在合适的时机关闭线程池:

  • executor.shutdown():拒绝新任务,等待已提交的任务完成后关闭线程池
  • executor.shutdownNow():尝试中断正在运行的任务,并立即关闭线程池

比如你可以把线程池设为全局变量,在主线程最后添加关闭逻辑,或者在calculate()方法提交任务后调用shutdown()。

额外说明

如果你改用CompletableFuture.supplyAsync()创建异步任务,默认会使用ForkJoinPool.commonPool(),这个池里的线程同样是非守护线程,所以依然会遇到相同问题,还是需要用上面的方法处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:30:57