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

将ScheduledExecutorService设为守护线程后,新增ExecutorService致程序无法退出

问题原因与解决方案

原因分析

你遇到的问题核心在于普通线程池的非守护线程阻止了JVM退出:

  • Executors.newFixedThreadPool(3)创建的线程池,默认使用的是非守护线程。当你调用executorService.execute(...)后,线程池会创建核心线程执行任务,任务完成后这些核心线程不会自动销毁(默认核心线程存活策略是一直等待新任务),作为非守护线程,它们会持续阻止JVM终止。
  • 你给ScheduledExecutorService设置了守护线程,这类线程不会阻止JVM退出,但只要有非守护线程在运行,JVM就不会终止,所以调度线程会继续执行。
  • 注释掉executorService.execute(...)时,线程池的核心线程还未被创建(线程池核心线程是懒加载的,有任务才会启动),此时没有非守护线程阻碍JVM,所以程序能正常退出。

解决方案

你可以通过以下几种方式解决这个问题:

1. 给普通线程池设置守护线程

和调度线程池一样,自定义线程工厂,把线程池里的线程都设为守护线程:

executorService = Executors.newFixedThreadPool(3, r -> {
    Thread thread = Executors.defaultThreadFactory().newThread(r);
    thread.setDaemon(true);
    return thread;
});

这样线程池里的线程都是守护线程,JVM准备退出时会强制终止它们,不会阻碍程序关闭。

2. 程序退出时主动关闭线程池

重写JavaFXApplication的stop方法,在窗口关闭时主动关闭所有线程池,销毁线程:

@Override
public void stop() throws Exception {
    // 先尝试优雅关闭
    executorService.shutdown();
    scheduledExecutorService.shutdown();
    
    // 等待线程池终止,超时则强制关闭
    if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
        executorService.shutdownNow();
    }
    if (!scheduledExecutorService.awaitTermination(1, TimeUnit.SECONDS)) {
        scheduledExecutorService.shutdownNow();
    }
    
    super.stop();
}

这种方式更规范,能保证线程池里的任务有机会完成收尾工作,适合需要优雅退出的场景。

3. 允许核心线程超时销毁

如果不需要核心线程一直存活,可以让核心线程在空闲超时后自动销毁:

// 强转为ThreadPoolExecutor,修改核心线程超时策略
ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
threadPool.allowCoreThreadTimeOut(true);
executorService = threadPool;

这样当线程池没有任务时,核心线程会在默认超时时间(60秒)后销毁,所有线程销毁后JVM就能正常退出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:20:03