You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:17:53