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

