如何用ExecutorService获取所有任务结果(含超时与失败处理)
使用ExecutorService实现带超时的多任务执行及结果处理
嘿,这个需求我熟!用ExecutorService结合Future完全能完美解决,我给你拆解清楚步骤,再附上可直接运行的代码示例,你一看就懂:
核心思路
- 用
Executors.newFixedThreadPool(5)创建固定大小的线程池,刚好匹配你的5个任务需求 - 把每个任务封装成
Callable实现类,这样既能返回执行结果,也能捕获执行过程中的异常 - 用
ExecutorService.invokeAll()方法批量提交任务并设置统一超时时间,这个方法比逐个调用future.get()更高效,还能统一处理超时场景 - 遍历返回的
Future集合,分别处理正常完成、超时、执行异常三种情况,给每个任务标记对应的状态(比如你要的Failed,还有Success、Timeout)
完整代码示例
首先定义任务状态枚举和任务类:
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.*; // 任务状态枚举,包含你需要的Failed状态 enum TaskStatus { SUCCESS, FAILED, TIMEOUT } // 自定义任务类,实现Callable接口,维护状态和结果 class Task implements Callable<String> { private final String taskName; private TaskStatus status; private String result; public Task(String taskName) { this.taskName = taskName; } @Override public String call() throws Exception { try { // 模拟任务执行:随机睡眠1-6秒,用来测试超时(假设超时时间设为5秒) int sleepTime = new Random().nextInt(6) + 1; Thread.sleep(sleepTime * 1000); // 模拟任务执行失败的场景:比如Task-3固定抛出异常 if ("Task-3".equals(taskName)) { throw new RuntimeException("数据库连接失败"); } return taskName + " 执行完成"; } catch (InterruptedException e) { // 响应线程中断:超时后线程会被中断,这里可以做清理工作 Thread.currentThread().interrupt(); // 保留中断状态,方便上层处理 throw e; } } // getter和setter方法,用于获取和设置状态、结果 public String getTaskName() { return taskName; } public TaskStatus getStatus() { return status; } public void setStatus(TaskStatus status) { this.status = status; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } } // 主类,演示执行流程 public class MultiTaskWithTimeout { public static void main(String[] args) { // 创建固定大小为5的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 设置超时时间:5秒 int timeoutSeconds = 5; List<Task> tasks = new ArrayList<>(); // 初始化5个任务 for (int i = 1; i <= 5; i++) { tasks.add(new Task("Task-" + i)); } try { // 提交所有任务,等待超时时间或所有任务完成 List<Future<String>> futures = executor.invokeAll(tasks, timeoutSeconds, TimeUnit.SECONDS); // 遍历每个任务,处理结果 for (int i = 0; i < tasks.size(); i++) { Task task = tasks.get(i); Future<String> future = futures.get(i); try { // 获取任务结果:如果任务正常完成,这里会返回结果 String result = future.get(); task.setStatus(TaskStatus.SUCCESS); task.setResult(result); } catch (TimeoutException e) { // 任务超时:超时后invokeAll会中断未完成的线程,这里标记状态为TIMEOUT task.setStatus(TaskStatus.TIMEOUT); task.setResult("任务超时,未完成"); System.out.println(task.getTaskName() + " 超时"); } catch (ExecutionException e) { // 任务执行失败:任务内部抛出异常,这里标记状态为FAILED task.setStatus(TaskStatus.FAILED); task.setResult("执行失败:" + e.getCause().getMessage()); System.out.println(task.getTaskName() + " 执行失败:" + e.getCause().getMessage()); } catch (InterruptedException e) { // 主线程被中断:这里可以根据业务需求处理 Thread.currentThread().interrupt(); task.setStatus(TaskStatus.FAILED); task.setResult("线程被中断"); } } // 输出所有任务的最终状态和结果 System.out.println("\n=== 所有任务执行结果 ==="); for (Task task : tasks) { System.out.printf("任务:%s | 状态:%s | 结果:%s%n", task.getTaskName(), task.getStatus(), task.getResult()); } } catch (InterruptedException e) { // 处理主线程被中断的情况 Thread.currentThread().interrupt(); System.err.println("主线程被中断,任务执行终止"); } finally { // 必须关闭线程池,避免资源泄漏 executor.shutdown(); try { // 等待线程池优雅关闭,最多等1分钟 if (!executor.awaitTermination(1, TimeUnit.MINUTES)) { // 超时后强制关闭线程池 executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } } } }
关键细节解释
invokeAll()的作用:
这个方法会一次性提交所有任务,并等待所有任务完成或者达到超时时间,超时后会自动中断未完成的线程。返回的Future集合和提交的任务是一一对应的,不管任务是否完成,你都能遍历处理。异常与状态处理:
- 当任务正常完成:
future.get()返回任务结果,标记状态为SUCCESS - 当任务超时:
future.get()抛出TimeoutException,标记状态为TIMEOUT - 当任务执行失败:任务内部抛出的异常会被包装成
ExecutionException,通过e.getCause()拿到原始异常,标记状态为FAILED
- 当任务正常完成:
线程中断的响应:
任务里的InterruptedException要正确处理,调用Thread.currentThread().interrupt()保留中断状态,这样上层代码能感知到中断。如果你的任务是IO密集型或者循环型的,最好在代码里定期检查Thread.currentThread().isInterrupted(),及时响应中断。线程池的关闭:
一定要在finally块里关闭线程池,shutdown()是优雅关闭(等待已提交的任务完成),awaitTermination()用来等待关闭完成,超时后用shutdownNow()强制关闭,避免资源泄漏。
内容的提问来源于stack exchange,提问作者voidMainReturn
相关产品推荐
相关产品推荐

