定时任务

开启定时任务

在配置类或启动类上加 @EnableScheduling,然后在任意受管 Bean 的方法上标注 @Scheduled 即可。

三种触发方式

@Component
public class ReportTask {

    // 固定频率:上次开始执行后 5 秒再执行下一次
    @Scheduled(fixedRate = 5000)
    public void every5s() { }

    // 固定延迟:上次执行结束后 5 秒再执行下一次
    @Scheduled(fixedDelay = 5000)
    public void delay5s() { }

    // 首次延迟 10 秒后开始,之后按 fixedDelay 运行
    @Scheduled(initialDelay = 10000, fixedDelay = 5000)
    public void afterStartup() { }

    // cron 表达式
    @Scheduled(cron = "0 0 2 * * *")   // 每天 02:00
    public void dailyReport() { }
}

cron 由 6 段组成(与 Linux/Quartz 的 5 段不同):秒 分 时 日 月 周。常用示例:

cron含义
0 0 2 * * *每天凌晨 2 点
0 */5 * * * *每 5 分钟
0 0 9 * * MON-FRI周一至周五 9 点

日期与星期的交叉限制等复杂规则,以及是否支持「年份」字段,以官方文档为准。需要显式指定时区时用 zone 属性:

@Scheduled(cron = "0 0 2 * * *", zone = "Asia/Shanghai")

cron 字符串支持属性占位符:@Scheduled(cron = "${task.report.cron:0 0 2 * * *}"),把表达式挪到配置文件更便于环境切换(见 05 章)。

线程模型

未自定义调度器时,Boot 自动配置的调度器默认单线程:多个 @Scheduled 任务会串行执行,一个任务耗时长会拖住其他任务。两个调整方向:

# 1) 调大自动配置的调度线程池
spring.task.scheduling.pool.size=4
// 2) 或自定义 TaskScheduler Bean,覆盖默认值
@Bean
public TaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setPoolSize(4);
    scheduler.setThreadNamePrefix("schedule-");
    return scheduler;
}

相关属性的确切前缀与自动配置后退规则以官方文档为准。

常见坑

  • 任务方法所在 Bean 必须被 Spring 管理;方法无参、无返回值(void),异常要在方法内自行处理避免影响调度器;
  • 任务里做耗时操作(如大批量报表)会长期占用线程,考虑异步化(见 21 章)拆分;
  • 多实例部署时每个实例都会执行同一份定时任务,需要分布式锁或统一调度平台(如 Quartz 集群方案)来保证只执行一次,属于部署架构问题,不在单机注解方案能力范围内;
  • cron 按服务器本地时区解析,跨时区部署记得用 zone

小结

@EnableScheduling + @Scheduled 是最轻量的定时方案:先分清 fixedRate/fixedDelay 的语义差别,再记住默认单线程、多实例会重复执行这两个坑,就可以安全落地大部分周期任务。

笔记加载中…