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

如何避免Cron任务重复执行?当前任务未完成时不触发新任务

分析你的Cron任务防重复执行方案是否符合需求

先明确你的两个核心需求:

  • 串行执行:当前任务未完成时,绝对不能触发新的任务实例
  • 时间对齐:首次执行在12:00:00,后续任务需从12:00:10开始(也就是按10秒固定间隔的时间点调度)

先说说你现有方案的问题

你代码里用future.isDone()来检查是否有任务在运行,这个逻辑完全无效。因为你用CronTrigger调度的是周期性任务,ScheduledFuture.isDone()只有在整个调度任务被取消、终止时才会返回true,正常运行期间它一直是false——也就是说这个判断根本没起到任何防重复的作用。

那为什么你的日志里看起来任务是串行的?其实是依赖了ThreadPoolTaskScheduler的默认特性:它底层的ScheduledThreadPoolExecutor默认核心线程数是1,所以即使Cron到点触发,新任务也会排队等待,只能串行执行。但这个依赖非常不可靠——如果后续你调整线程池大小(比如设置taskScheduler.setPoolSize(3)),立刻就会出现多个任务实例并行执行的情况,完全违反你的第一个需求。

你的日志验证

从你给出的日志来看,任务确实是串行的,但这是线程池排队的结果,不是你的代码逻辑控制的。比如日志里出现了scheduler task1、scheduler task2、scheduler task3不同线程名,说明线程池已经创建了多个线程,但因为核心线程数只有1,新任务只能排队,所以没有并行。但这不是稳定的防重复机制。

满足需求的正确实现方式

要同时满足你的两个需求,需要做两部分调整:

1. 添加可靠的互斥锁,强制串行

在你的RunnableTask里加入ReentrantLock,用tryLock()尝试获取锁,获取不到就直接跳过当前触发的任务——这样无论线程池有多少线程,同一时间只有一个任务能执行核心逻辑:

private class RunnableTask implements Runnable {
    private Task task;
    private int times;
    private final ReentrantLock executionLock = new ReentrantLock(); // 互斥锁

    private RunnableTask(Task task) {
        this.task = task;
    }

    private void println(String string) {
        System.out.println(Thread.currentThread().getName() + string + " timestamp:" + LocalTime.now().format(DateTimeFormatter.ofPattern("mm:ss")));
    }

    @Override
    public void run() {
        // 尝试获取锁,获取不到则跳过当前任务
        if (!executionLock.tryLock()) {
            println(" skip: " + task.getId() + " times: " + times + " (previous task still running)");
            return;
        }
        try {
            println(" begin: " + task.getId() + " times: " + times);
            // 替换为你的文件读写操作
            FileInputStream fis = new FileInputStream("D:\\test\\test.exe");
            FileOutputStream fos = new FileOutputStream("D:\\test\\a-" + times + "");
            fos.write(IOUtils.toByteArray(fis));
            fos.flush();
            fos.close();
            fis.close();
            println(" end: " + task.getId() + " times: " + times);
            times++;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executionLock.unlock(); // 必须释放锁,避免死锁
        }
    }
}

2. 调整Cron表达式对齐10秒间隔

你原来的Cron是0/3 * * * * *(每3秒执行一次),这和你“后续从12:00:10开始”的需求不符。要实现固定10秒间隔的时间点调度,应该用:

Task t1 = new Task(1L, "0/10 * * * * ?", 10000L);

这个Cron表达式表示每10秒执行一次,从0秒开始(也就是12:00:00、12:00:10、12:00:20...这些时间点触发)。

额外选项:如果允许下一次执行依赖上一次结束时间

如果你的需求不是严格对齐固定时间点,而是“上一次任务结束后10秒再执行下一次”,那可以不用CronTrigger,改用scheduleWithFixedDelay,这种方式天然保证串行,不需要额外加锁:

// 首次延迟0秒执行,之后每次任务结束后延迟10秒再执行
ScheduledFuture sf1 = taskScheduler.scheduleWithFixedDelay(new RunnableTask(t1), 0, 10, TimeUnit.SECONDS);

总结

你的现有方案不满足需求,当前的串行是线程池默认行为带来的巧合,不是可靠的防重复机制。必须添加互斥锁逻辑,同时调整Cron表达式(或改用固定延迟调度),才能真正满足你的两个核心需求。

内容的提问来源于stack exchange,提问作者xmcx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:12:34