如何实现@Scheduled定时任务在指定方法执行后启动与停止
实现结论
完全可以实现你的按需启停定时任务、节省系统资源的需求,不需要引入第三方组件,用Spring原生的动态调度能力就能完成改造。
你最初的写法之所以做不到按需启停,是因为把@Scheduled直接标注在Bean方法上、搭配类级别的@EnableScheduling,会让Spring容器在启动阶段就把这个任务自动注册到调度器,只要应用不退出就会一直运行,完全没有手动管控的入口。
具体改造步骤
- 移除目标任务方法上硬编码的
@Scheduled注解,避免容器启动时自动注册任务 - 注入Spring内置的
TaskScheduler组件,这个组件是Spring提供的、用来动态提交和取消定时任务的核心入口,只要开启了@EnableScheduling,容器就会自动装配好这个Bean - 在类里定义一个成员变量,用来存储当前运行中定时任务的引用句柄(类型为
ScheduledFuture),后续停止任务的时候,需要靠这个句柄找到对应的任务 - 在你自定义的
triggerStart方法里,判断任务是否在运行,如果没在运行就手动提交固定频率的任务,把返回的任务句柄存到成员变量里 - 在你自定义的
triggerFinish方法里,判断任务是否在运行,如果在运行就通过之前存的句柄调用取消方法,把任务停掉
改造后的完整代码
@EnableScheduling @Slf4j public class Scheduling { // 存储当前运行中定时任务的句柄,用于后续停止操作 private ScheduledFuture<?> scheduledTaskFuture; // 注入Spring自动装配的任务调度器 @Autowired private TaskScheduler taskScheduler; // 目标任务方法,移除原有的@Scheduled注解 public void scheduledTask() { log.info("scheduled task has been started"); } public void triggerStart() { // 避免重复启动已在运行的任务 if (scheduledTaskFuture != null && !scheduledTaskFuture.isCancelled()) { log.info("定时任务已在运行,无需重复启动"); return; } log.info("after this @Scheduled task will start working"); // 手动提交固定频率1秒的定时任务,存储返回的任务句柄 scheduledTaskFuture = taskScheduler.scheduleAtFixedRate( this::scheduledTask, 1000 ); } public void triggerFinish() { // 任务不存在或已停止时直接返回,避免空指针 if (scheduledTaskFuture == null || scheduledTaskFuture.isCancelled()) { log.info("定时任务未在运行,无需停止"); return; } log.info("after this @Scheduled task will stop working"); // 取消运行中的任务,入参true表示强制中断正在执行的任务 scheduledTaskFuture.cancel(true); } }
实操注意事项
- 千万别忘了移除原
scheduleTask方法上的@Scheduled注解,不然Spring启动时依然会自动把任务注册到调度器,达不到按需启动、节省资源的效果 - 调用
cancel方法时的入参可以根据业务实际调整:传false时,只会取消还没到触发时间的待执行任务,已经在运行的任务会等执行完成后再正常退出;传true时会直接中断正在执行的任务,根据业务的中断容忍度选择即可 - 这套方案完全基于Spring原生能力实现,未触发启动的任务根本不会被提交到调度线程池,完全不会占用额外的系统资源,符合你的设计目标
内容的提问来源于stack exchange,提问作者Serhii Kachan
相关产品推荐
相关产品推荐

