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

相关推荐
吴梓穆1 分钟前
UE5 c++ 常用方法
java·c++·ue5
王夏奇23 分钟前
python中的__all__ 具体用法
java·前端·python
明湖起风了29 分钟前
mqtt消费堆积
java·jvm·windows
Free Tester1 小时前
如何判断 LeakCanary 报告的严重程度
java·jvm·算法
清心歌1 小时前
CopyOnWriteArrayList 实现原理
java·开发语言
Java成神之路-2 小时前
通俗易懂理解 Spring MVC 拦截器:概念、流程与简单实现(Spring系列16)
java·spring·mvc
zhanghongbin012 小时前
AI 采集器:Claude Code、OpenAI、LiteLLM 监控
java·前端·人工智能
计算机毕设vx_bysj68692 小时前
【免费领源码】77196基于java的手机银行app管理系统的设计与实现 计算机毕业设计项目推荐上万套实战教程JAVA,node.js,C++、python、大屏数据可视化
java·mysql·智能手机·课程设计
忘梓.2 小时前
墨色规则与血色节点:C++红黑树设计与实现探秘
java·开发语言·c++
hhh3u3u3u2 小时前
Visual C++ 6.0中文版安装包下载教程及win11安装教程
java·c语言·开发语言·c++·python·c#·vc-1