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

ExecutorService awaitTermination未等待线程完成即终止主线程问题

问题:ExecutorService调用shutdown和awaitTermination后主线程未等待任务完成就终止

我创建了一个包含3个线程的ExecutorService,用来处理每个包含200万个对象的列表。提交3个任务后,我调用了以下方法:

executorService.shutdown();
executorService.awaitTermination(20, TimeUnit.HOURS);

但3个线程开始执行后,主线程并未等待处理线程完成就直接终止了。请问我忽略了什么?


相关代码

ExecutorService executorService = new ThreadPoolExecutor(3, 3, 1L, TimeUnit.MINUTES, 
new LinkedBlockingQueue<>(BLOCKING_QUEUE_SIZE), new ThreadPoolExecutor.CallerRunsPolicy());

List<List<String>> listOfList = new ArrayList<>(); // 每个内部列表包含200万个元素
for (List<String> list : listOfList) {
    executorService.submit(() -> processList(list));
}

executorService.shutdown();            
final boolean awaitTermination = executorService.awaitTermination(20, TimeUnit.HOURS);
if (awaitTermination) {
    System.out.print("Await Termination returned True\n");
} else {
    System.out.print("Await Termination returned False\n");
}

System.out.printf("Terminating main thread!\n");

processList方法实现

private void processList(List<String> list) {

final String uuid = UUID.randomUUID().toString();
System.out.printf("UUID - %s\n", uuid);

// 处理列表,此过程约耗时1小时

System.out.printf("UUID - %s - Completed processing!\n", uuid);
}

程序输出

UUID - 448553ca-93ba-459e-a864-a3f1bf2aadf1
UUID - da43f632-ff75-42d2-9244-bc3c1fb834ea
UUID - 9f028167-eef0-4017-903e-9246d8670dee
Await Termination returned True
Terminating main thread!

问题根源与解决方案

从输出可见,任务仅打印了启动日志,未输出完成日志,但awaitTermination返回true,说明ExecutorService判定所有任务已执行完成。核心问题在于:

1. submit提交的任务异常被静默吞噬

使用submit(Runnable)时,任务执行过程中抛出的未捕获异常会被封装到返回的Future对象中,但你没有保存这些Future,也没有调用Future.get()来获取结果,导致异常被隐藏。ExecutorService会将异常任务标记为“已完成”,因此awaitTermination会提前返回true,主线程终止,而异常的任务会悄悄停止。

2. 可选排查点:线程池队列与拒绝策略

若BLOCKING_QUEUE_SIZE过小,结合CallerRunsPolicy,部分任务可能在主线程执行,若主线程在shutdown()前就完成了这些任务,也可能出现类似情况,但从输出看三个任务都在子线程启动,此概率较低。


修复方案

方案一:改用execute提交任务

execute方法提交的任务,未捕获异常会直接输出到控制台,便于快速定位问题:

for (List<String> list : listOfList) {
    executorService.execute(() -> processList(list));
}

方案二:保存Future并处理异常

若坚持用submit,需保存每个任务的Future,遍历调用get()捕获异常:

List<Future<?>> futures = new ArrayList<>();
for (List<String> list : listOfList) {
    futures.add(executorService.submit(() -> processList(list)));
}

executorService.shutdown();            
executorService.awaitTermination(20, TimeUnit.HOURS);

// 遍历Future获取结果,捕获任务异常
for (Future<?> future : futures) {
    try {
        future.get();
    } catch (InterruptedException | ExecutionException e) {
        System.err.println("任务执行异常:");
        e.printStackTrace();
    }
}

方案三:设置线程池未捕获异常处理器

给线程池添加UncaughtExceptionHandler,统一捕获子线程的未捕获异常:

ThreadPoolExecutor executorService = new ThreadPoolExecutor(3, 3, 1L, TimeUnit.MINUTES, 
new LinkedBlockingQueue<>(BLOCKING_QUEUE_SIZE), new ThreadPoolExecutor.CallerRunsPolicy());

// 设置异常处理器
executorService.setUncaughtExceptionHandler((thread, throwable) -> {
    System.err.printf("线程[%s]执行异常:%s\n", thread.getName(), throwable.getMessage());
    throwable.printStackTrace();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:23:21