@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分钟)且需要严格按时间点执行时可以使用,但需要做好防重叠和线程池配置。

相关推荐
yaonoran2 小时前
【无标题】
java·开发语言·变量
康小庄2 小时前
浅谈Java中的volatile关键字
java·开发语言·jvm·spring boot·spring·jetty
vx_bisheyuange2 小时前
基于SpringBoot的海鲜市场系统
java·spring boot·后端·毕业设计
康康的AI博客2 小时前
工业数据中台:PLC、SCADA、MES的实时协同架构
java·服务器·网络
それども2 小时前
为什么要加@ResponseBody
java·开发语言·spring boot
一只专注api接口开发的技术猿2 小时前
微服务架构下集成淘宝商品 API 的实践与思考
java·大数据·开发语言·数据库·微服务·架构
2501_944424123 小时前
Flutter for OpenHarmony游戏集合App实战之记忆翻牌配对消除
android·java·开发语言·javascript·windows·flutter·游戏
鹿角片ljp3 小时前
Java网络编程入门:从Socket到多线程服务器
java·服务器·网络
走进IT3 小时前
DDD项目分层结构说明
java