关于@Scheduled注解的任务为什么不执行的问题

 更新时间:2022年09月30日 15:35:39   作者:融极  
这篇文章主要介绍了关于@Scheduled注解的任务为什么不执行的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

概述

在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注解实现的定时任务,到此我们刚才的问题就可以理解了。

因为是单个线程执行所有的定时任务,所有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... ...

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Mybatis输入输出映射及动态SQL Review

    Mybatis输入输出映射及动态SQL Review

    这篇文章主要介绍了Mybatis输入输出映射及动态SQL Review,需要的朋友可以参考下
    2017-02-02
  • Spring Boot前后端分离开发模式中的跨域问题及解决方法

    Spring Boot前后端分离开发模式中的跨域问题及解决方法

    本文介绍了解决Spring Boot前端Vue跨域问题的实战经验,并提供了后端和前端的配置示例,通过配置后端和前端,我们可以轻松解决跨域问题,实现正常的前后端交互,需要的朋友可以参考下
    2023-09-09
  • SpringBoot自动配置原理详解

    SpringBoot自动配置原理详解

    SpringBoot的诞生就是为了简化Spring中繁琐的XML配置,其本质依然还是Spring框架,使用SpringBoot之后可以不使用任何XML配置来启动一个服务,使得我们在使用微服务架构时可以更加快速的建立一个应用。本文将为具体介绍一下SpringBoot的原理,需要的可以参考一下
    2021-12-12
  • 小白教程! Linux服务器上JDK安装配置方法

    小白教程! Linux服务器上JDK安装配置方法

    这篇文章主要为大家详细介绍了Linux服务器上JDK安装配置方法,小白教程!具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • 关于@Controller和@Restcontroller的那点奇葩事

    关于@Controller和@Restcontroller的那点奇葩事

    这篇文章主要介绍了关于@Controller和@Restcontroller的那点奇葩事,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Mybatis单个参数的if判断报异常There is no getter for property named ''xxx'' in ''class java.lang.Integer''的解决方案

    Mybatis单个参数的if判断报异常There is no getter for property named ''x

    今天小编就为大家分享一篇关于Mybatis单个参数的if判断报异常There is no getter for property named 'xxx' in 'class java.lang.Integer'的解决方案,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 使用springboot aop来实现读写分离和事物配置

    使用springboot aop来实现读写分离和事物配置

    这篇文章主要介绍了使用springboot aop来实现读写分离和事物配置,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04
  • java中Pulsar InterruptedException 异常

    java中Pulsar InterruptedException 异常

    这篇文章主要为大家介绍了java中Pulsar InterruptedException 异常分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • MyBatis流式查询的项目实践

    MyBatis流式查询的项目实践

    本文主要介绍了MyBatis流式查询的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 重新启动IDEA时maven项目SSM框架文件变色所有@注解失效

    重新启动IDEA时maven项目SSM框架文件变色所有@注解失效

    这篇文章主要介绍了重新启动IDEA时maven项目SSM框架文件变色所有@注解失效,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03

最新评论