SpringBoot 3.1中ThreadPoolTaskScheduler任务取消后等待终止方案
解决方案
方案一:基于ScheduledFuture.isDone()的等待(简单快速)
这种方案无需修改任务逻辑,仅需在取消后循环等待任务完成:
ScheduledFuture<?> scheduleFuture = commandManagerMap.get(key); // 取消后续调度,但允许当前正在运行的任务完成 scheduleFuture.cancel(false); // 等待任务完成,通过短暂休眠避免忙等 while (!scheduleFuture.isDone()) { try { Thread.sleep(100); } catch (InterruptedException e) { // 恢复线程中断状态 Thread.currentThread().interrupt(); throw new RuntimeException("等待任务终止时被中断", e); } } // 任务已完全执行完毕,执行后续操作 doSomething();
说明:
cancel(false)仅阻止任务的后续调度,不会中断当前正在运行的任务;当当前任务执行完毕后,scheduleFuture.isDone()会返回true。Thread.sleep(100)用于避免忙等占用CPU,可根据业务对实时性的要求调整休眠时长。
方案二:改造任务添加显式等待机制(更可靠优雅)
如果需要更精确的任务状态控制,可改造MyTask添加线程安全的状态跟踪和等待方法:
1. 改造MyTask类
public class MyTask implements Runnable { private final ReentrantLock lock = new ReentrantLock(); private final Condition taskCompleted = lock.newCondition(); // 标记当前任务是否正在运行 private volatile boolean isRunning = false; // 标记任务是否被取消(停止后续调度) private volatile boolean isCancelled = false; @Override public void run() { lock.lock(); try { // 若已被取消,直接退出,不再执行本轮逻辑 if (isCancelled) { return; } isRunning = true; } finally { lock.unlock(); } try { // 执行业务逻辑:先存TableA,再存TableB saveToTableA(); saveStatusToTableB(); } finally { lock.lock(); try { isRunning = false; // 通知等待的线程:当前任务已完成 taskCompleted.signalAll(); } finally { lock.unlock(); } } } /** * 标记任务为取消状态,并等待当前正在运行的任务终止 */ public void cancelAndWait() throws InterruptedException { lock.lock(); try { isCancelled = true; // 循环等待避免虚假唤醒,直到当前任务结束 while (isRunning) { taskCompleted.await(); } } finally { lock.unlock(); } } // 业务方法示例 private void saveToTableA() { /* 实际存入TableA的逻辑 */ } private void saveStatusToTableB() { /* 实际存入TableB的逻辑 */ } }
2. 修改任务存储结构
需要同时存储ScheduledFuture和对应的MyTask实例,可创建一个持有类:
public class TaskHolder { private final ScheduledFuture<?> scheduledFuture; private final MyTask task; public TaskHolder(ScheduledFuture<?> scheduledFuture, MyTask task) { this.scheduledFuture = scheduledFuture; this.task = task; } // getter方法 public ScheduledFuture<?> getScheduledFuture() { return scheduledFuture; } public MyTask getTask() { return task; } }
3. 任务调度时存入Holder
MyTask myTask = new MyTask(); ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.scheduleAtFixedRate(myTask, Duration.ofMillis(2000)); commandManagerMap.put(key, new TaskHolder(scheduledFuture, myTask));
4. 控制器中取消并等待
TaskHolder taskHolder = commandManagerMap.get(key); if (taskHolder != null) { // 取消后续调度 taskHolder.getScheduledFuture().cancel(false); try { // 等待当前任务执行完毕 taskHolder.getTask().cancelAndWait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("等待任务终止时被中断", e); } // 任务完成后执行后续操作 doSomething(); }
说明:
- 采用
ReentrantLock和Condition实现线程安全的等待/通知机制,避免无效的CPU占用。 isCancelled标记确保任务被取消后不会启动新的一轮执行。- 最终块中更新状态并通知等待线程,保证业务逻辑全部执行完毕后才会唤醒控制器线程。
内容的提问来源于stack exchange,提问作者Andrea P.
相关产品推荐
相关产品推荐

