Java:如何并行执行多个Runnable并超时终止长运行任务?
单个任务独立超时的线程池实现方案
要实现每个任务独立5秒超时(而非全局总超时),同时避免主线程阻塞,可通过以下思路解决:
- 单独提交任务并关联ID与Future:放弃
invokeAll的全局超时机制,逐个提交任务到线程池,用Map记录每个任务ID对应的Future对象。 - 异步定时监控超时:借助
ScheduledExecutorService为每个任务安排5秒后的延迟检查,若任务未完成则取消并记录ID。 - 处理任务取消逻辑:任务被中断时捕获
InterruptedException,在任务内部标记超时状态,主线程统一收集超时任务ID。
修改后的代码示例:
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.*; public class TestExecutorService { // 核心线程数匹配CPU核心数,满足并行执行需求 private static final ExecutorService taskExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static final ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); private static final List<Integer> timeoutTaskIds = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws InterruptedException { Map<Integer, Future<?>> taskFutureMap = new HashMap<>(); for (int i = 0; i < 100; i++) { int taskId = i; int minSleep = 1; int maxSleep = 10; Runnable task = () -> { try { int sleepTime = minSleep + (int) (Math.random() * ((maxSleep - minSleep) + 1)); System.out.printf("## 任务ID: %d 预计耗时: %d 秒%n", taskId, sleepTime); Thread.sleep(sleepTime * 1000); System.out.printf("## 任务ID: %d 正常完成,耗时: %d 秒%n", taskId, sleepTime); } catch (InterruptedException e) { System.out.printf("## 任务ID: %d 因超时被终止%n", taskId); timeoutTaskIds.add(taskId); } }; // 提交任务并保存Future与任务ID的映射 Future<?> future = taskExecutor.submit(task); taskFutureMap.put(taskId, future); // 安排5秒后的定时任务,检查并取消未完成任务 timeoutExecutor.schedule(() -> { Future<?> f = taskFutureMap.get(taskId); if (f != null && !f.isDone()) { // 中断正在运行的任务 f.cancel(true); } }, 5, TimeUnit.SECONDS); } // 关闭任务线程池,等待所有任务完成 taskExecutor.shutdown(); taskExecutor.awaitTermination(20, TimeUnit.SECONDS); // 关闭定时监控线程池 timeoutExecutor.shutdown(); // 输出超时任务列表 System.out.println("====================="); System.out.println("超时被终止的任务ID列表:"); timeoutTaskIds.forEach(id -> System.out.print(id + " ")); } }
关键逻辑说明:
- 线程池配置:
taskExecutor核心线程数与CPU核心数一致,符合"4个CPU同时运行4个任务"的并行要求。 - 无阻塞监控:
timeoutExecutor作为单线程定时池,异步处理每个任务的超时检查,不会阻塞主线程。 - 任务中断处理:
Future.cancel(true)会中断任务的睡眠操作,触发InterruptedException,任务内部捕获后记录超时ID。 - 线程安全存储:使用
CopyOnWriteArrayList存储超时任务ID,避免多线程写入时的并发冲突。
内容的提问来源于stack exchange,提问作者Bằng Rikimaru
相关产品推荐
相关产品推荐

