@Scheduled(cron = “0 */5 * * * ?“) 详解

@Scheduled(cron = "0 /5 ?") 执行原理详解

  1. Cron 表达式解析

"0 */5 * * * ?"

│ │ │ │ │ └─ 星期几(?表示不指定)

│ │ │ │ └─── 月份(*表示每月)

│ │ │ └───── 日期(*表示每日)

│ │ └─────── 小时(表示每小时)
│ └────────── 分钟(
/5表示每5分钟)

└───────────── 秒(0表示整秒触发)

触发时间示例:

• 14:00:00

• 14:05:00

• 14:10:00

• 14:15:00

• ...

  1. 执行原理时序图

Spring 应用启动

@EnableScheduling 扫描 @Scheduled 注解

注册 CronTask 到 TaskScheduler

TaskScheduler 内部线程定时检查 cron 表达式

匹配时间 → 触发任务执行

  1. 线程行为分析

默认配置(单线程)

// Spring 默认创建单线程调度器

ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

scheduler.setPoolSize(1); // 默认只有1个线程!

执行情况:

时间线:

14:00:00 - 线程A开始执行task3()

14:02:00 - task3()还在执行

14:05:00 - cron时间到,但线程A被占用,task3()排队等待

14:07:00 - 线程A完成任务 → 立即执行排队的task3()

问题:任务会排队堆积,实际执行间隔不是严格的5分钟!

自定义线程池配置

@Configuration

@EnableScheduling

public class SchedulerConfig {

@Bean

public TaskScheduler taskScheduler() {

ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

scheduler.setPoolSize(5); // 多线程

return scheduler;

}

}

执行情况:

时间线:

14:00:00 - 线程A执行task3()

14:05:00 - 线程B执行task3()(并行执行)

14:10:00 - 线程C执行task3()(并行执行)

  1. 与其他调度方法的区别

调度方式 触发时机 线程行为 适用场景

fixedDelay 上次任务结束后固定时间 严格不重叠 数据同步

fixedRate 固定频率,不等待完成 可能重叠 监控任务

cron 绝对时间点 可能排队/重叠 定时报表

  1. 实际代码演示

@Component

@Slf4j

public class CronTaskDemo {

复制代码
private final AtomicInteger executionCount = new AtomicInteger(0);

@Scheduled(cron = "0 */5 * * * ?")  // 每5分钟整点执行
public void task3() {
    int count = executionCount.incrementAndGet();
    String threadName = Thread.currentThread().getName();
    long startTime = System.currentTimeMillis();
    
    log.info("=== 第{}次执行开始 [线程:{}] ===", count, threadName);
    
    // 模拟任务执行(假设需要2分钟)
    try {
        Thread.sleep(120000);  // 2分钟
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    
    long cost = System.currentTimeMillis() - startTime;
    log.info("=== 第{}次执行结束 [耗时:{}ms] ===", count, cost);
}

}

运行结果(默认单线程):

14:00:00 - === 第1次执行开始 [线程:pool-1-thread-1] ===

14:02:00 - === 第1次执行结束 [耗时:120000ms] ===

14:05:00 - === 第2次执行开始 [线程:pool-1-thread-1] === ← 准时触发

14:07:00 - === 第2次执行结束 [耗时:120000ms] ===

运行结果(任务耗时超过5分钟的情况):

14:00:00 - === 第1次执行开始 [线程:pool-1-thread-1] ===

14:05:00 - cron时间到,但线程被占用,任务排队

14:07:00 - === 第1次执行结束 [耗时:420000ms] ===

14:07:00 - === 第2次执行开始 [线程:pool-1-thread-1] === ← 立即执行排队任务

  1. 数据同步场景的特殊考虑

风险:任务执行时间 > 5分钟

@Scheduled(cron = "0 */5 * * * ?")

public void dataSync() {

// 总耗时可能超过5分钟!

}

解决方案

@Component

@Slf4j

public class SafeCronDataSync {

复制代码
private final AtomicBoolean isRunning = new AtomicBoolean(false);

@Scheduled(cron = "0 */5 * * * ?")
public void dataSync() {
    // 防止任务重叠
    if (!isRunning.compareAndSet(false, true)) {
        log.warn("前一个数据同步任务仍在执行,跳过本次cron触发");
        return;
    }
    
    try {
        // 实际数据同步逻辑
        doDataSync();
    } finally {
        isRunning.set(false);
    }
}

private void doDataSync() {
    long start = System.currentTimeMillis();
    
    // 1. 查询数据库
    List<Map<String, Object>> data = queryData();
    
    // 2. 异步发送Kafka(不阻塞cron线程)
    CompletableFuture.runAsync(() -> {
        sendToKafka(data);
    });
    
    long cost = System.currentTimeMillis() - start;
    log.info("数据同步任务完成,耗时: {}ms", cost);
}

}

  1. 最佳实践总结

  2. cron适合按绝对时间点执行,如整点报表

  3. 数据同步推荐使用fixedDelay,避免时间偏差累积

  4. 必须配置多线程池,避免任务排队

  5. 必须添加防重叠机制,防止数据重复处理

  6. Kafka发送要用异步,不阻塞调度线程

对于你的场景,推荐:

@Scheduled(fixedDelay = 5 * 60 * 1000, initialDelay = 10000) // 更合适

public void dataSync() {

// 防重叠 + 异步发送

}

@Scheduled(cron = "0 */5 * * * ?") 在任务执行时间可控(<3分钟)且需要严格按时间点执行时可以使用,但需要做好防重叠和线程池配置。

相关推荐
砍材农夫16 分钟前
物联网 基于netty构建mqtt协议规范(遗嘱与保留消息)
java·开发语言·物联网·netty
DFT计算杂谈19 分钟前
KPROJ编译教程
java·前端·python·算法·conda
重生之我是Java开发战士31 分钟前
【笔试强训】Week5:空调遥控, kotor和气球,走迷宫,主持人调度II,体操队形,二叉树的最大路径和,排序子序列,消减整数
java·算法·动态规划
郑重其事,鹏程万里1 小时前
表达式计算器(mvel2)
java
其实防守也摸鱼1 小时前
软件安全与漏洞--软件安全编码
java·前端·网络·安全·网络安全·web·工具
888CC++1 小时前
栈上分配 VS 堆分配 核心区别
java·开发语言·jvm
艾利克斯冰1 小时前
Java面试题汇总
java
我是一颗柠檬2 小时前
【JavaSE全面教学】Java集合框架下Day13(2026年)
java·开发语言·intellij-idea
vx-程序开发2 小时前
基于机器学习的动漫可视化系统的设计与实现-计算机毕业设计源码08339
java·c++·spring boot·python·spring·django·php
LCG元2 小时前
RAG工程指南:从基础检索到生产部署全解析
java·运维·数据库