微信优化

weixinyouhua

Spring定时任务配置有哪些方式,quartz和注解区别,怎么选?

2026-11-08 17:50:47

Spring定时任务配置最快能跑起来的方式,是主类加@EnableScheduling、方法上加@Scheduled,三行代码搞定;但真正决定它能不能上生产的,是线程池、cron时区和分布式重复执行这三件事。

Spring定时任务配置有哪些方式,quartz和注解区别,怎么选?

最小可用配置:十分钟跑通第一个定时任务

Spring把定时任务做得很轻,轻到很多人第一次用会怀疑是不是漏配了什么。

三步开启

  1. 启动类或任意@Configuration类上加@EnableScheduling,这是总开关,不加的话所有@Scheduled方法都是摆设。
  2. 写一个@Component类,方法上加@Scheduled。
  3. 确认方法无参、返回void,且所在Bean被Spring管理。
@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
@Component
public class ReportJob {
    @Scheduled(cron = "0 0 2   ?", zone = "Asia/Shanghai")
    public void buildDailyReport() {
        // 业务逻辑
    }
}

zone这个属性建议显式写上,服务器时区是UTC时,你的凌晨两点任务会老老实实在北京时间上午十点跑,这类问题排查起来很费时间。

cron表达式怎么读

Spring的cron是六位(秒 分 时 日 月 周),支持七位(末尾加年),几个常用写法:

  • 0 0 2 ? 每天凌晨2点
  • 0 /5 ? 每5分钟
  • 0 0 9 ? MON-FRI 工作日早上9点
  • 0 30 1 1 ? 每月1号1点30

只能出现在“日”和“周”其中一位,因为这两个字段互斥,写成这种六星形式,多数情况下会被Spring拒绝启动,报“cron表达式非法”。

Spring定时任务cron表达式和fixedRate到底有什么区别

这是新手最容易纠结的一组选择,选错了轻则任务堆积,重则把数据库打满。

四种调度方式的真实差异

方式 触发规则 适用场景 要注意的点
cron 按日历时间点触发 日报、对账、清理类任务 时区必须显式指定
fixedRate 按固定频率触发,从上一次开始时间算 心跳、轮询 任务耗时长会重叠
fixedDelay 上一次结束后延迟固定时间 串行处理队列 周期会被拉长
initialDelay 首次延迟,配合上面三者 避免启动瞬间打满 常和fixedDelay搭配

关键区别在参照点:fixedRate盯着“上一次开始时间”,fixedDelay盯着“上一次结束时间”,一个任务平均跑8秒,你配了fixedRate=5000,那它永远在追赶,线程池里会堆任务,改成fixedDelay=5000,节奏立刻稳下来。

业内专家指出,定时任务的频率配置本质是“业务能容忍多晚”和“系统能承受多快”之间的平衡,而不是技术参数的堆砌。

选型的一句话原则

  • 业务关心“几点执行”,用cron。
  • 业务关心“隔多久执行一次”,用fixedDelay。
  • 只有在明确知道任务执行时间远小于间隔时,才用fixedRate。

Spring Boot定时任务配置多线程怎么开启

这个问题问得很多,因为默认行为确实容易踩坑。

默认只有单线程

不配置的话,Spring会用一个单线程的调度器跑所有@Scheduled方法,两个任务都定在整点,第二个必须等第一个跑完,你以为是并行,实际是排队。

验证方法很简单:写两个各睡眠3秒的任务,配同一个cron,观察日志时间戳是否重叠。

Spring定时任务配置有哪些方式,quartz和注解区别,怎么选?

两种开启方式

方式一,Spring Boot 2.1之后最省事,配置文件两行:

spring.task.scheduling.pool.size=8
spring.task.scheduling.thread-name-prefix=sched-

方式二,需要精细控制优雅停机时,用代码配置:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(8);
        scheduler.setThreadNamePrefix("sched-");
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        scheduler.setAwaitTerminationSeconds(30);
        scheduler.initialize();
        registrar.setTaskScheduler(scheduler);
    }
}

线程池大小别拍脑袋,多数情况下,池子大小等于“同一时刻可能触发的任务数”,再加上一点冗余就够了,开到几十上百,只是把压力转移到下游数据库。

setWaitForTasksToCompleteOnShutdown(true)这一行很值得加,否则应用停机时正在跑的任务会被直接掐断,跑到一半的批量更新就成了脏数据。

Spring定时任务分布式部署重复执行怎么解决

单机时代没人关心这个问题,一旦部署两个实例,同一个凌晨对账任务就跑了两次,账目直接翻倍。

四种主流方案的取舍

  • ShedLock:加一个数据库表存锁记录,注解式使用,侵入小,适合实例数不多、已有数据库的中小型项目。
  • Redis分布式锁:用SET key value NX PX 30000抢锁,性能好,要注意锁续期和释放的原子性,不然会出死锁。
  • XXL-JOB、PowerJob这类调度中心:把调度权收归中心,执行器只负责跑,适合任务多、需要可视化管理、失败重试和告警的团队。
  • 云厂商托管调度:按量计费,省运维,长三角和珠三角不少团队在业务快速扩张期会选这条路,代价是绑定了平台。

成本这笔账要算清楚

选型时绕不开成本,自建调度中心省下的是订阅费和云服务账单,花掉的是运维人力、机器资源和故障响应时间,托管服务反过来,行业共识认为,任务数量在几十个以内、团队没有专职中间件运维时,托管或轻量锁方案的总体成本更低;任务上百、调度逻辑复杂时,自建调度中心的长期收益更明显。

如果是在评估外包报价,注意区分“只配几个@Scheduled”和“要接入分布式调度并做监控告警”,两者工作量差好几倍,报价自然不在一个档位。

ShedLock的最小示例

@Scheduled(cron = "0 0 2   ?")
@SchedulerLock(name = "buildDailyReport",
        lockAtMostFor = "30m", lockAtLeastFor = "1m")
public void buildDailyReport() {
    // 同一时刻只会有一个实例进入
}

lockAtMostFor是兜底,防止持锁实例宕机后锁永不释放。lockAtLeastFor是防抖,避免任务执行过快导致多实例在锁间隙里轮流进入。

生产环境绕不开的几个实操细节

配置跑通只是起点,下面这些才是事故高发区。

异常必须自己兜住

@Scheduled方法抛异常,Spring默认只打一行日志,任务下次照跑,但这次的数据就丢了,建议在方法体内部try-catch,把失败任务落到补偿表,配一个重试任务扫表。

Spring定时任务配置有哪些方式,quartz和注解区别,怎么选?

别让定时任务和业务抢连接池

凌晨批量任务一开,数据库连接瞬间打满,早高峰的用户请求跟着超时,给批量任务单独配数据源,或者限制单次处理条数、分批提交,是很常见的做法。

加一条执行日志

至少记录任务名、开始时间、结束时间、处理条数、是否成功,出问题时这几列信息比任何监控面板都直接。

优雅停机要演练

配置了等待时间不等于万事大吉,上线前手动kill一次进程,看看正在跑的任务是正常收尾还是被截断,比看文档靠谱。

写在最后

Spring定时任务配置的门槛很低,@EnableScheduling加@Scheduled就能跑;但把它放进生产环境的门槛不低,线程池、时区、幂等、异常、停机这五件事一件都不能省,先把最小配置跑通,再按业务量决定要不要上分布式调度,这个顺序比一上来就堆中间件更省事。

关于Spring定时任务配置的常见疑问

Spring定时任务配置后为什么不执行

按顺序排查四处:启动类有没有@EnableScheduling;任务类是否被Spring扫描到;cron表达式是否合法(六位或七位,的位置对不对);方法是否为public void且无参,还有一个隐蔽原因类被自己new出来了,没走容器,自然不生效。

Spring定时任务和Quartz、XXL-JOB怎么选

只做单机、任务数量少、不需要失败重试和可视化管理,用Spring自带的@Scheduled足够,需要持久化、集群、任务依赖编排,选Quartz或调度中心,判断标准是任务数量和管理诉求,不是框架新旧。

定时任务的时间不准是怎么回事

先看服务器时区和JVM时区是否一致,再看任务本身耗时是否超过了触发间隔导致排队,用fixedRate配短间隔跑长任务时,延迟会越积越多,改用fixedDelay或者扩大线程池都能缓解。

相关文章

2024年,SaaS软件行业碰到获客难、增长慢等问题吗?

我们努力让每一次邂逅总能超越期待