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

当包含类准备被GC时优雅关闭ThreadPoolExecutor的实现方案

未关闭线程池的影响
  • 线程资源泄漏:ThreadPoolExecutor的工作线程默认是非守护线程,只要这些线程存活,JVM就无法正常退出。哪怕主服务类被GC回收,线程池里的空闲线程仍会一直等待新任务,持续占用内存和CPU资源,长期积累会耗尽系统资源。
  • 关联资源无法释放:线程池内部的任务队列、锁、统计组件等资源也不会被回收,时间久了会引发内存泄漏,严重时直接导致OOM。
  • 意外任务风险:如果其他代码误引用到这个未关闭的线程池,可能会提交非预期任务,打乱正常业务逻辑。
不用finalize时优雅关闭线程池的方案

1. 虚引用+引用队列监听GC触发关闭

虚引用是最弱的引用类型,当对象被GC标记回收时,对应的虚引用会被放入关联的ReferenceQueue。我们可以通过监听这个队列来触发线程池的关闭逻辑:

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.ThreadPoolExecutor;

public class ServiceWithThreadPool {
    private final ThreadPoolExecutor executor;
    private final PoolCleanupReference selfReference;
    private static final ReferenceQueue<ServiceWithThreadPool> referenceQueue = new ReferenceQueue<>();

    static {
        // 启动守护线程监听引用队列,不阻止JVM退出
        Thread cleanupThread = new Thread(() -> {
            while (true) {
                try {
                    Reference<? extends ServiceWithThreadPool> ref = referenceQueue.remove();
                    if (ref instanceof PoolCleanupReference) {
                        ((PoolCleanupReference) ref).cleanupThreadPool();
                        ref.clear();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.start();
    }

    public ServiceWithThreadPool(ThreadPoolExecutor executor) {
        this.executor = executor;
        this.selfReference = new PoolCleanupReference(this, referenceQueue);
    }

    private static class PoolCleanupReference extends PhantomReference<ServiceWithThreadPool> {
        private final ThreadPoolExecutor targetExecutor;

        public PoolCleanupReference(ServiceWithThreadPool service, ReferenceQueue<ServiceWithThreadPool> queue) {
            super(service, queue);
            this.targetExecutor = service.executor;
        }

        public void cleanupThreadPool() {
            // 优雅关闭:拒绝新任务,等待已提交任务执行完成
            targetExecutor.shutdown();
            try {
                // 等待60秒,超时则强制终止未完成任务
                if (!targetExecutor.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS)) {
                    targetExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                targetExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
}

2. 显式管理生命周期(生产环境首选)

依赖GC触发关闭存在不确定性,更可靠的方式是主动管理线程池生命周期:给服务类添加关闭方法,或实现AutoCloseable接口配合try-with-resources自动关闭:

public class ServiceWithThreadPool implements AutoCloseable {
    private final ThreadPoolExecutor executor;

    public ServiceWithThreadPool(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    @Override
    public void close() throws Exception {
        executor.shutdown();
        // 等待任务完成,超时强制终止
        if (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS)) {
            executor.shutdownNow();
        }
    }
}

// 使用时通过try-with-resources自动关闭线程池
try (ServiceWithThreadPool service = new ServiceWithThreadPool(executor)) {
    // 执行业务逻辑
} // 代码块结束自动调用close()

这种方式完全可控,不受GC时机影响,是生产环境的最优选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 10:25:33