ExecutorService shutdown后未执行全部任务问题
问题根因
- 对
ExecutorService.shutdown()方法的行为认知错误:该方法仅会将线程池置为关闭状态,拒绝新任务提交,不会阻塞等待队列中已提交的数千个任务全部执行完成。调用shutdown后立刻读取innerArray长度时,大量排队的任务还未被调度执行,因此会出现长度远小于预期、任务看似被跳过的现象,仅调整线程池线程数无法修复等待逻辑缺失的问题。 - 共享集合线程不安全:常规代码中使用的
ArrayList属于非线程安全实现,多线程并发调用add()方法时,会出现元素覆盖、size计数不准确、底层数组越界等问题,即便所有任务都执行完成,并发写入也会造成数据丢失,导致集合长度不符合预期。
修复方案
核心调整两点:一是替换为线程安全的集合存储多线程写入的结果,二是增加等待逻辑,确保所有提交的任务全部执行完成后再读取集合长度。
import java.util.*; import java.util.concurrent.*; // 省略业务上下文定义... // 1. 使用线程安全的列表存储结果,避免并发写入异常 List<YourResultType> innerArray = Collections.synchronizedList(new ArrayList<>()); ExecutorService executorService = Executors.newFixedThreadPool(3); List<Future<?>> allTasks = new ArrayList<>(); outerArray.forEach(entry -> { // 提交任务时保留返回的Future对象,用于后续等待任务完成 Future<?> task = executorService.submit(() -> innerArray.add(updateEntry(params...))); allTasks.add(task); }); try { // 2. 遍历所有任务等待执行完成,可根据业务场景设置合理超时时间 for (Future<?> task : allTasks) { task.get(10, TimeUnit.MINUTES); } } catch (InterruptedException | ExecutionException | TimeoutException e) { // 按需处理任务执行异常、中断、超时场景 Thread.currentThread().interrupt(); throw new RuntimeException("批量任务执行失败", e); } finally { executorService.shutdown(); // 兜底等待线程池资源释放 try { if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } // 所有任务执行完成后读取innerArray长度,此时长度与outerArray一致
补充说明
- 若不想遍历所有Future等待,也可以在调用
shutdown()后直接调用awaitTermination()方法阻塞等待线程池执行完所有任务,注意必须设置合理的超时时间,避免业务线程无限阻塞。 - 线程安全的结果存储容器除了
Collections.synchronizedList包装的ArrayList,也可以根据业务场景选择CopyOnWriteArrayList、ConcurrentLinkedQueue等并发容器,禁止在多线程写入场景下直接使用普通ArrayList。
内容的提问来源于stack exchange,提问作者Dikum
相关产品推荐
相关产品推荐

