线程任务抛出异常时终止ExecutorService后续任务并捕获异常的方案咨询
解决方案
问题根因
你原有代码的问题出在两点:
- 向ExecutorService提交
Callable任务后,异常会被封装在返回的Future对象中,你没有主动调用Future.get()方法,所以异常直接被吞掉,外层catch自然捕获不到任何内容。 - 你提交完所有任务后直接调用
threadPool.shutdown(),会等待所有已提交的任务执行完成,自然不会停止未开始的任务。
常见误解澄清
你担心的「等待Future返回会阻塞其余线程运行」是完全不存在的:线程池的worker线程是独立运行的,主线程调用ExecutorCompletionService.take()只是按完成顺序获取已结束的任务结果,不会对正在运行的worker线程产生任何阻塞影响,完全不会违背多线程并发设计的初衷。
实现代码
你只需要调整原有主线程的逻辑,提交完任务后循环拉取已完成的任务结果,一旦捕获到异常立刻终止线程池即可:
import java.util.concurrent.*; public class Test { public static void main(String[] args) { final ExecutorService threadPool = Executors.newFixedThreadPool(4); final ExecutorCompletionService<Void> completionService = new ExecutorCompletionService<>(threadPool); int taskCount = 10; try { for (int i = 0; i < taskCount; i++) { int b = i; completionService.submit(() -> doSomeWork(b)); } // 循环拉取已完成的任务,共taskCount个 for (int i = 0; i < taskCount; i++) { Future<Void> finishedFuture = completionService.take(); finishedFuture.get(); // 这里会抛出任务执行时的异常 } // 所有任务正常完成才会走到这里 threadPool.shutdown(); threadPool.awaitTermination(1, TimeUnit.HOURS); System.exit(0); } catch (ExecutionException e) { // 捕获到任务抛出的异常 System.out.println("Something wrong happened: " + e.getCause().getMessage()); // 立刻终止线程池:清空等待队列的未执行任务,给正在运行的线程发中断信号 threadPool.shutdownNow(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); threadPool.shutdownNow(); } System.exit(1); } //该函数有50%概率抛出异常 public static Void doSomeWork(int i) throws Exception { Thread.sleep(500); if ((Math.random() > 0.5)) { System.out.println("I have reached indice: " + i); } else { throw new Exception("I couldn't handle indice " + i); } return null; } }
效果说明
以上代码运行后完全符合你的预期:
- 只要有任意一个任务抛出异常,主线程会立刻捕获到异常
- 调用
shutdownNow()后,所有还在队列中未开始执行的任务会被直接清空,不会再运行 - 正在运行的任务如果包含
Thread.sleep这类响应中断的逻辑,会立刻抛出InterruptedException终止运行,实现最快停止的效果
内容的提问来源于stack exchange,提问作者Mit94
相关产品推荐
相关产品推荐

