为什么使用@Scheduled注解的任务未能按预期执行?

2026-05-25 21:231阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1681个文字,预计阅读时间需要7分钟。

为什么使用@Scheduled注解的任务未能按预期执行?

目录概述示例说明原因分析解决方案概述在SpringBoot中,可以通过@Scheduled注解定义定时任务。但有时会发现,尽管定时任务设置了执行时间,却未按预期执行。

示例说明javaimport org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;

@Componentpublic class ScheduledTask {

@Scheduled(cron=0 0/1 * * * ?) public void scheduledTask() { System.out.println(定时任务执行); }}

原因分析

1.时区设置错误:SpringBoot默认使用系统时区,如果服务器时区设置错误,可能导致定时任务时间计算错误。

2.任务执行时间与服务器时间不同步:服务器时间与任务执行时间不一致,可能导致任务未执行。

3.服务器时间调整:服务器时间调整后,可能导致定时任务执行时间与实际时间不符。

解决方案

1.检查时区设置:确保服务器时区与任务执行时间时区一致。

2.同步服务器时间:使用NTP(Network Time Protocol)同步服务器时间。

3.调整任务执行时间:根据服务器时间调整任务执行时间,确保任务在预期时间执行。

目录
  • 概述
  • 举例说明
  • 原因分析
  • 解决方案

概述

在SpringBoot中可以通过@Scheduled来注解定义一个定时任务,但是有时候你可能发现有的定时任务道理时间却没有执行,但是又不是每次都不执行,为什么呢???

举例说明

下面这段diam定义了一个没隔10s执行一次的定时任务:

package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /**  * @Description : schedule测试  * @Version : V1.0.0  * @Date : 2021/12/1 11:22  */ @Component @Slf4j public class ScheduleTest {     @Scheduled(cron = "0/10 * * * * ?")     public void execute() {         log.info("Scheduled task is running ... ...");     } }

记得在启动类或者Configuration类上添加了@EnableScheduling注解。

启动应用,控制台每隔10秒打印一条日志:

2021-12-01 11:27:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:10.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:20.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:30.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:40.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:50.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:10.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...

但是,问题才刚刚开始。

在上面的相关代码中,我们使用cron表达式指定的定时任务执行时间点从0秒开始,每隔10s执行一次,现在我们再加一个定时任务:

package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /**  * @Description : schedule测试  * @Version : V1.0.0  * @Date : 2021/12/1 11:22  */ @Component @Slf4j public class ScheduleTestTwo {     @Scheduled(cron = "0/10 * * * * *")     public void second() throws InterruptedException {         log.info("Second scheduled task is running... ...");         TimeUnit.SECONDS.sleep(5);     } }

启动项目,执行结果如下:

2021-12-01 11:36:30.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:30.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:40.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:45.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:50.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:55.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:37:00.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:37:05.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...

可以看到任务1和任务2本该都是每个10s执行,但是却发现只有任务2执行了,任务1却等待了5s之后才执行。

原因分析

为了找到原因,我们从@Scheduled注解的源码开始分析:

下面是@Scheduled注解的注释:

 *  * <p>Processing of {@code @Scheduled} annotations is performed by  * registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be  * done manually or, more conveniently, through the {@code <task:annotation-driven/>}  * element or @{@link EnableScheduling} annotation.  *

划重点, 每一个有@Scheduled注解的方法都会被注册为一个ScheduledAnnotationBeanPostProcessor, 再接着往下看ScheduledAnnotationBeanPostProcessor:

    /**      * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke      * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}      * to be wrapped as a TaskScheduler.      * <p>If not specified, default scheduler resolution will apply: searching for a      * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}      * bean named "taskScheduler" otherwise; the same lookup will also be performed for      * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,      * a local single-threaded default scheduler will be created within the registrar.      * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME      */     public void setScheduler(Object scheduler) {         this.scheduler = scheduler;     }

重点来了,注意这句话:

if neither of two is resolvable, a local single-thread default scheduler will be created within in the registrar.

这句话意味着,如果我们不主动配置我们需要的TaskScheduler,SpringBoot会默认使用一个单线程的scheduler来处理我们用@Scheduled注解实现的定时任务,到此我们刚才的问题就可以理解了。

为什么使用@Scheduled注解的任务未能按预期执行?

因为是单个线程执行所有的定时任务,所有task2如果先执行,因为执行中等待了5s,所以task2执行完后,task1接着继续执行。

解决方案

搞清楚这个流程后,解决这个问题就很简单了。

根据刚才注释的描述,我们只需要提供一个满足自己需要的TaskScheduler并注册到context容器中就可以了。

package com.study.practice.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; /**  * @Description : scheduler配置类  * @Version : V1.0.0  * @Date : 2021/12/1 14:00  */ @Configuration public class ScheduledTaskConfig implements SchedulingConfigurer {     @Override     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {         ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();         taskScheduler.setPoolSize(2);         taskScheduler.initialize();         taskRegistrar.setTaskScheduler(taskScheduler);     } }

上面的代码提供了一个线程池大小为2的taskScheduler,再次启动SpringBoot查看效果。

2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...

可以看到,当线程池里有两个线程时,这两个任务各种按照预定的时间进行触发,互不影响了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

本文共计1681个文字,预计阅读时间需要7分钟。

为什么使用@Scheduled注解的任务未能按预期执行?

目录概述示例说明原因分析解决方案概述在SpringBoot中,可以通过@Scheduled注解定义定时任务。但有时会发现,尽管定时任务设置了执行时间,却未按预期执行。

示例说明javaimport org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;

@Componentpublic class ScheduledTask {

@Scheduled(cron=0 0/1 * * * ?) public void scheduledTask() { System.out.println(定时任务执行); }}

原因分析

1.时区设置错误:SpringBoot默认使用系统时区,如果服务器时区设置错误,可能导致定时任务时间计算错误。

2.任务执行时间与服务器时间不同步:服务器时间与任务执行时间不一致,可能导致任务未执行。

3.服务器时间调整:服务器时间调整后,可能导致定时任务执行时间与实际时间不符。

解决方案

1.检查时区设置:确保服务器时区与任务执行时间时区一致。

2.同步服务器时间:使用NTP(Network Time Protocol)同步服务器时间。

3.调整任务执行时间:根据服务器时间调整任务执行时间,确保任务在预期时间执行。

目录
  • 概述
  • 举例说明
  • 原因分析
  • 解决方案

概述

在SpringBoot中可以通过@Scheduled来注解定义一个定时任务,但是有时候你可能发现有的定时任务道理时间却没有执行,但是又不是每次都不执行,为什么呢???

举例说明

下面这段diam定义了一个没隔10s执行一次的定时任务:

package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /**  * @Description : schedule测试  * @Version : V1.0.0  * @Date : 2021/12/1 11:22  */ @Component @Slf4j public class ScheduleTest {     @Scheduled(cron = "0/10 * * * * ?")     public void execute() {         log.info("Scheduled task is running ... ...");     } }

记得在启动类或者Configuration类上添加了@EnableScheduling注解。

启动应用,控制台每隔10秒打印一条日志:

2021-12-01 11:27:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:10.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:20.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:30.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:40.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:50.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:10.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...

但是,问题才刚刚开始。

在上面的相关代码中,我们使用cron表达式指定的定时任务执行时间点从0秒开始,每隔10s执行一次,现在我们再加一个定时任务:

package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /**  * @Description : schedule测试  * @Version : V1.0.0  * @Date : 2021/12/1 11:22  */ @Component @Slf4j public class ScheduleTestTwo {     @Scheduled(cron = "0/10 * * * * *")     public void second() throws InterruptedException {         log.info("Second scheduled task is running... ...");         TimeUnit.SECONDS.sleep(5);     } }

启动项目,执行结果如下:

2021-12-01 11:36:30.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:30.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:40.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:45.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:50.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:55.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:37:00.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:37:05.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...

可以看到任务1和任务2本该都是每个10s执行,但是却发现只有任务2执行了,任务1却等待了5s之后才执行。

原因分析

为了找到原因,我们从@Scheduled注解的源码开始分析:

下面是@Scheduled注解的注释:

 *  * <p>Processing of {@code @Scheduled} annotations is performed by  * registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be  * done manually or, more conveniently, through the {@code <task:annotation-driven/>}  * element or @{@link EnableScheduling} annotation.  *

划重点, 每一个有@Scheduled注解的方法都会被注册为一个ScheduledAnnotationBeanPostProcessor, 再接着往下看ScheduledAnnotationBeanPostProcessor:

    /**      * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke      * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}      * to be wrapped as a TaskScheduler.      * <p>If not specified, default scheduler resolution will apply: searching for a      * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}      * bean named "taskScheduler" otherwise; the same lookup will also be performed for      * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,      * a local single-threaded default scheduler will be created within the registrar.      * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME      */     public void setScheduler(Object scheduler) {         this.scheduler = scheduler;     }

重点来了,注意这句话:

if neither of two is resolvable, a local single-thread default scheduler will be created within in the registrar.

这句话意味着,如果我们不主动配置我们需要的TaskScheduler,SpringBoot会默认使用一个单线程的scheduler来处理我们用@Scheduled注解实现的定时任务,到此我们刚才的问题就可以理解了。

为什么使用@Scheduled注解的任务未能按预期执行?

因为是单个线程执行所有的定时任务,所有task2如果先执行,因为执行中等待了5s,所以task2执行完后,task1接着继续执行。

解决方案

搞清楚这个流程后,解决这个问题就很简单了。

根据刚才注释的描述,我们只需要提供一个满足自己需要的TaskScheduler并注册到context容器中就可以了。

package com.study.practice.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; /**  * @Description : scheduler配置类  * @Version : V1.0.0  * @Date : 2021/12/1 14:00  */ @Configuration public class ScheduledTaskConfig implements SchedulingConfigurer {     @Override     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {         ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();         taskScheduler.setPoolSize(2);         taskScheduler.initialize();         taskRegistrar.setTaskScheduler(taskScheduler);     } }

上面的代码提供了一个线程池大小为2的taskScheduler,再次启动SpringBoot查看效果。

2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...

可以看到,当线程池里有两个线程时,这两个任务各种按照预定的时间进行触发,互不影响了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。