将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
相关产品推荐
相关产品推荐

