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

Java调用CompletionService.take()后程序挂起无法退出问题排查

问题根因

通过Executors.newCachedThreadPool()创建的线程池,默认内置线程工厂生成的是非守护类用户线程。JVM进程正常终止的判断标准是所有用户线程全部执行完毕,而示例代码在拿到异步任务返回结果后,未主动关闭持有的线程池:

  • ExecutorCompletionService只是异步任务结果的封装组件,实际持有工作线程资源的是构造时传入的线程池实例
  • CachedThreadPool默认给空闲线程设置了60秒的存活等待期,空闲阶段线程会一直阻塞等待新任务提交,不会主动销毁
  • 只要存在存活的用户线程,JVM就不会退出,因此业务逻辑输出完成后程序会一直挂起。即便等待60秒等空闲线程被自动回收、进程退出,这种依赖线程自动超时回收的写法也不符合线程池使用规范,存在资源泄漏风险。
修改方案

在确认所有任务处理完成、不需要再提交新任务时,主动调用线程池的关闭方法释放线程资源即可。修改后可正常执行退出的代码如下:

public static void main(String[] args) {
    // 单独持有线程池实例,用于后续资源释放
    ExecutorService threadPool = Executors.newCachedThreadPool();
    CompletionService<Integer> cs = new ExecutorCompletionService<>(threadPool);
    cs.submit(new Callable<Integer>() {
        public Integer call(){
            try{
                Thread.sleep(3000); // 睡眠3秒后打印
                System.out.println("Sleeping thread: " + Thread.currentThread().getId());
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            return 10;
        }
    });
    try{
        Future<Integer> fi = cs.take();
        System.out.println(fi.get());
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        // 任务执行完成后平滑关闭线程池
        threadPool.shutdown();
    }
}

补充说明:shutdown()会执行平滑关闭逻辑:不再接收新提交的任务,等待池中已提交的所有任务执行完成后,销毁全部工作线程释放资源。如果需要立即中断所有运行中的任务终止线程池,可以使用shutdownNow()方法,日常开发无特殊需求优先使用平滑关闭方式。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:03:28