Springboot自带定时器使用和原理解析

原创
2021/03/13 16:11
阅读数 1.3K

Springboot定时任务使用和原理解析

  • 大型项目中一般会用到quartz进行定时任务的管理,但是一些小型目不需要进行分布式部署或者简单的任务则可以使用Springboot自带的@Scheduled来进行实现。我们一块来看下是如何使用以及实现的原理

一、Springboot定时任务使用

1. 简单配置定时任务

1). 在Springboot启动类添加注释@EnableScheduling 例如:

@EnableScheduling
public class Application{

}

2). 在执行的方式上使用@Scheduled注解 例如:

@Scheduled(cron = "0 0/1 * * * ?")
    public void cronJob() {
}

这样就搞定了

2. 通过@Configuration配置

第一步 将启动类的@EnableScheduling去掉

第二步 添加配置类SchedulerConfig

@Configuration
@ConditionalOnProperty("scheduling.enabled")
public class SchedulerConfig {

    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}
  1. 在application.yml 添加配置
scheduling:
  enabled: true #默认不启动定时器true 启动false 不启动

这种方式可以通过配置文件来控制定时任务是否启用

  1. @ConditionalOnProperty用法: 配合@Configuration进行使用,当ConditionalOnProperty的为true时当前配置类生效

二、定时器原理解析

先说原理:

  1. Springboot定时任务是通过jdk java.util.concurrent包的ScheduledThreadPoolExecutor实现,
  2. 本次任务开始执行时,会确定下次任务的执行时间,将任务提交到ScheduledThreadPoolExecutor中,以达到循环执行的效果

所以想要了解原理必须先了解ScheduledThreadPoolExecutor。总结一句话就是 :ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后执行任务,或者定期执行任务。

我们就以ScheduledAnnotationBeanPostProcessor 这个类为起点看下源码是如何实现的

  • 第一步 加载@Scheduled注解信息 ScheduledAnnotationBeanPostProcessor初始化完成调用postProcessAfterInitialization,扫描带有注解 @Scheduled的,并把缓存起来(根据bean的声明周期postProcessAfterInitialization会在bean初始化完成后执行,)。 不了解Spring bean的初始化过程的可以看下图

源码如下:

  • 第二步:实例化完成后进行服务注册

ScheduledAnnotationBeanPostProcessor加载并实例化后触发onApplicationEvent finishRegistration() -> afterPropertiesSet 进行服务注册,任务最终由ReschedulingRunnable的schedule添加到了线程池中的

  1. 调用onApplicationEvent
  2. ScheduledTaskRegistrar的scheduledCornTask进行的服务注册
  3. ScheduledTaskRegistrar中进行任务注册
  4. ConcurrentTaskScheduler 的schedule方法
  5. 最终在ReschedulingRunnable的schedule()方法中提交了ScheduledThreadPoolExecutor进行执行 到此任务注册完成,指定时间会有线程池进行执行。
  • 第三步: 循环执行逻辑

本次任务开始执行时,会确定下次任务的执行时间,并将任务提交到ScheduledThreadPoolExecutor中,以达到循环执行的效果

以上这就是Springboot定时器实现的全部逻辑。经过分析要注意一下两个问题

  1. ScheduledThreadPoolExecutor 是使用Executors.newSingleThreadScheduledExecutor();创建,所有定时任务会共用这一个线程池,并且线程池的核心线程数是1,任务执行不及时,会造成其他任务的延时。
  2. 如果系统是分布式,那定时任务不能同时开启。不然造成任务的重复执行
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部