Spring 中的 @Scheduled 注解的作用是什么?
- 标签:后端、Spring
🏛 业务架构师
@Scheduled 的作用是声明周期性定时任务:标在方法上、开启 @EnableScheduling 后,按 cron 表达式或固定间隔自动执行。用法如 @Scheduled(cron = "0 0 2 * * ?") 或 fixedDelay/fixedRate + initialDelay。
业务架构师视角,我用它的边界很明确——单机轻量任务可以,企业级调度必须上调度平台:
- 单机风险:默认单线程串行执行,一个任务卡死后面全延迟;多实例部署时每个节点都会跑一遍,对账任务重复执行就是资损事故。所以我的纪律:多实例环境必须加分布式锁(Redis/DB)或用
ShedLock; - 缺乏管理能力:没有失败重试、告警、手动触发、执行历史、阻塞队列——而"账单日任务失败且无人知道"是实打实的业务事故。XXL-Job、PowerJob 这类平台把这些变成标配:可视化、分片广播、失败告警、依赖编排;
- 任务治理:公司层面我把任务当资产管理——谁负责、失败了谁收到告警、幂等吗(重跑是否安全),这些是比"能不能跑"更重要的评审项。
所以我的标准回答是:知道 @Scheduled 怎么用(Demo、后台小工具、单实例内部系统),但生产任务的选型默认是调度平台,@Scheduled 只留在"允许重复执行且幂等"的场景。
🏗 系统架构师
作用:声明式定时任务——@Scheduled(cron/fixedRate/fixedDelay) 配合 @EnableScheduling,cron 支持六位表达式(秒分时日月周)。
必须主动讲的三个坑(也是区分度所在):
- 默认单线程调度器:多个任务串行执行,一个慢任务拖死全部——必须调
spring.task.scheduling.pool.size或按业务隔离调度线程池; - 分布式重复执行:多实例部署时每个节点都会跑。解决:ShedLock/Redisson 分布式锁,或任务调度平台(xxl-job:路由、失败转移、控制台)——我的默认是 xxl-job 这类平台,锁方案只救小场景;
- 自愈与幂等:misfire 策略与任务重跑幂等必须设计,否则补跑就是生产事故。
架构选型:简单延迟任务用 @Scheduled;企业级定时(编排依赖、告警审计、灰度)上调度平台;K8s 环境 CronJob 适合「跑完即退」的批处理,与常驻服务的内部定时互补。
💻 全栈开发专家
声明式定时任务:@EnableScheduling 开总开关,然后:
java
@Component
public class ReportTask {
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
public void dailyReport() { ... }
@Scheduled(fixedDelay = 5000) // 上次【结束】5s 后再跑
public void sync() { ... }
@Scheduled(fixedRate = 3000, initialDelay = 10_000)
public void poll() { ... } // 固定频率,延迟10s启动
}fixedDelay vs fixedRate 必须分清:前者间隔从上次完成算起,任务慢会顺延;后者固定频率,任务超时长会排队追赶。
生产三大坑:
- 默认单线程调度器:一个任务阻塞,其他全饿死。配
spring.task.scheduling.pool.size=5或任务内异步化 - 多实例重复执行:K8s 3 个副本同一 cron 都跑——对账任务重复出报表的事故真见过。解法:Redisson
tryLock抢到才执行,或上 xxl-job(调度中心统一管、失败重试、手动补跑) - 异常静默:任务抛异常只打日志下次照跑,必须包 try-catch + 告警
排查:jstack 看 scheduling- 线程卡在哪;cron 表达式用在线生成器校验,手写错一位就是事故。