最后更新:2026-10-28
适用场景:Spring Boot 3.x、@Scheduled、Jar / 容器部署
定时清理在本地每天早上九点正常跑,部署后却像消失了一样。另一个环境恰好相反:每次都跑两遍。先别急着换 Quartz。这两种现象通常可以从任务有没有被注册、触发时间怎么算、实例到底有几个三件事里找到答案。
先把“等到九点”改成五秒一次
在测试环境用一个只记日志、不改业务数据的任务排除时间因素:
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Component
public class ScheduleProbe {
private static final Logger log =
LoggerFactory.getLogger(ScheduleProbe.class);
@Scheduled(fixedDelay = 5000)
public void tick() {
log.info("schedule probe, thread={}", Thread.currentThread().getName());
}
}
上面省略了常规 import,两个类分别放在自己的 Java 文件中。启动后等十几秒,应该看到多次 schedule probe。确认后删除探针,别让它在生产环境长期刷日志。Spring Framework 的调度文档明确要求启用调度,并由容器管理带 @Scheduled 的 Bean。
如果探针也没动,先查 @EnableScheduling 是否在实际启动的配置中;ScheduleProbe 是否在启动类的包或子包里;类是否有 @Component 或经 @Bean 注册。把对象自己 new 出来,注解不会因此被 Spring 扫到。包结构的基本规则可参考 Spring Boot 官方说明。
探针能跑,原任务不跑:看触发条件
Spring 的 cron 是 六段,第一段是秒,不是 Linux crontab 的五段。例如工作日九点:
@Scheduled(cron = "0 0 9 * * MON-FRI", zone = "Asia/Shanghai")
public void sendReport() {
// 业务逻辑
}
在服务器上先打印当前时间和时区,再核对任务配置。上面的 zone 让 cron 按上海时间解释,不会依赖容器默认时区。fixedDelay 是上一次执行结束后再等指定时长;fixedRate 则按开始时间间隔安排。任务本身跑了三分钟,却用一分钟 fixedDelay,并不会每分钟触发一次。
还要检查配置占位符是否读到了预期值、线上激活了哪个 profile。比如 cron 写在 application-prod.yml,线上没有启用 prod,你改这份文件不会改变运行中的任务。类似环境问题可对照Spring Boot 配置文件怎么写。
任务注册了,却一直轮不到它
Spring Boot 3.5 在普通平台线程配置下,自动配置的 ThreadPoolTaskScheduler 默认只有 1 个线程。一个长时间阻塞的定时任务,可能把别的任务排在后面。先在每个任务开始和结束时记录时间、线程名与耗时,看看是不是某个任务一直没结束。
确认确实需要并行后,再评估线程池大小:
spring:
task:
scheduling:
pool:
size: 2
别把线程数直接调到几十。并行后可能出现共享数据竞争,原任务若没有幂等保护,跑得更快不一定更对。使用 Java 21 虚拟线程模式时,调度器实现与这些池配置也不同,应以实际配置为准。Spring Boot 调度说明列出了这些差异。
跑两遍,先数实例
如果同一条日志在同一秒出现两次,带上 实例名、容器 ID 或主机名 再看一次。两个应用实例各自启动了 @Scheduled,就会各自执行;这不是 Spring 在一个实例里“重复触发”。多实例只允许运行一次的任务,需要分布式锁、独立调度服务,或只在单独的 worker 实例启用,不能靠单机 synchronized 解决。
若确认只有一个实例,检查是否把同一个任务类注册成了两个 Bean,或同一方法上重复声明了多个 @Scheduled。Spring 会分别处理重复的调度声明。排查时先观察 Bean 数量和触发日志,不要为了止住重复执行而直接删一半业务逻辑。
最后做一次贴近生产的复测
把探针恢复为真实 cron,在测试环境明确时区和启动 profile;检查预期时间前后各一次日志。任务若会写数据库或发消息,再做一次重复触发测试,确认幂等逻辑。日志本身找不到时,可以接着看Spring Boot 日志文件没生成怎么排查。
参考资料
Spring Framework 6.2:Task Execution and Scheduling
Spring Boot 3.5:Task Execution and Scheduling
更新记录
2026-10-28:创建文章,补充最小探针、时区、线程池与多实例排查。
Spring Boot 定时任务不执行或执行两次怎么排查
https://java.li/archives/spring-boot-scheduled-not-running
评论