很多情况下任务并非需要立即执行,而是需要在指定时间或指定频率执行,这不可能人工去操作,所以定时任务就出现了。

定时任务四种实现方案

  • Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。

  • ScheduledExecutorService:也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

  • Spring Task:Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

  • Quartz:这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。

  • 为什么不选用quartz?

    虽然quartz性能更强,但spring Task比较简单,写了一个demo可以满足需求,就选用这个。当时项目赶进度,就使用这个了。 先有后优

本文主要介绍Spring Task的使用和实现原理。

Spring Task特点

SprngTask没有专门的包,是Spring 3.0自带的定时任务,其核心类位于spring-context包中,所以引入spring的核心包此功能即可使用。可以将它看作成一个轻量级的Quartz,功能虽然没有Quartz那样强大,但是使用起来非常简单,无需增加额外的依赖,可直接上手使用。它具备如下特点

  • 默认单线程同步执行
    SpringTask 默认是单线程的,不同定时任务使用的都是同一个线程;当存在多个定时任务时,若当前任务执行时间过长则可能导致下一个任务无法执行。

    在实际开发中,不希望所有的任务都运行在一个线程中。可在配置类中使用ScheduledTaskRegistrar#setTaskScheduler(TaskScheduler taskScheduler),SpringTask提供一个基于多线程的TaskScheduler接口的实现类(譬如ThreadPoolTaskScheduler类),Spring默认使用ConcurrentTaskScheduler。

  • 对异常的处理
    在SpringTask中,一旦某个任务在执行过程中抛出异常,则整个定时器生命周期就结束,以后永远不会再执行定时器任务。需要手动处理异常

  • 默认不适用分布式环境
    Spring Task 并不是为分布式环境设计的,在分布式环境下,这种定时任务是不支持集群配置的,如果部署到多个节点上,各个节点之间并没有任何协调通讯机制,集群的节点之间是不会共享任务信息的,每个节点上的任务都会按时执行,导致任务的重复执行。

    我们可以使用支持分布式的定时任务调度框架,比如 Quartz、XXL-Job、Elastic Job

    当然你可以借助 zookeeperredis 等实现分布式锁来处理各个节点的协调问题。

    或者把所有的定时任务抽成单独的服务单独部署。 本项目即是需要使用分布式锁

在spring boot中Spring Task的三种使用方式

基于@Scheduled注解(静态调度,单线程,串行执行)

使用该方式只需完成两步操作

  1. 使用@EnableScheduling注解开启定时任务,该注解放在启动类上。

    @SpringBootApplication
    @EnableScheduling //开启定时任务
    public class ScheduledDemoApplication
    {public static void main(String[] args){SpringApplication.run(ScheduledDemoApplication.class, args);}
    }
    
  2. 使用@Scheduled注解定义定时任务

    public class SaticScheduleTask {//定义定时任务@Scheduled(cron = "0/5 * * * * ?")private void configureTasks() {System.err.println("执行静态定时任务时间: " + LocalDateTime.now());}
    }
    

    其中,使用这个注解的方法必需无参,无返回值,若有返回值将被忽略。

    @Scheduled注解的参数有以下四种,分别对应四种任务调度策略

    • cron表达式

      “秒 分 时 天 月 周”,其他语法参照资料

      cron表达式配置了在哪一刻执行任务,会在配置的任务开始时间判断任务是否可以执行,如果能则执行,不能则会跳过本次执行;每次任务不一定都会执行。
      强调在某时某分某刻执行定时任务

    • fixedDelay (固定的延迟,结束-开始的间隔

      设定上一个任务结束后多久执行下一个任务,即上一任务的结束时间和下一任务的开始时间

      每次任务都会执行

    • fixedRate (固定的频率,开始-开始的间隔
      设定上一个任务开始后多久执行下一个任务,即上一个任务的开始时间到下一个任务开始时间的间隔

      特别地,若到达任务的开始执行时间,但上一个任务却没有完成时,spring会等待上一个任务执行完,并立即开始执行本次任务。

      每次任务都会执行

    • initialDelay(初始化延迟)
      需要配合fixedDelay或fixedRate使用
      设定延迟多长时间后开始执行第一次定时任务,其后按照fixedDelay或fixedRate的逻辑执行。

需要注意的是,由于默认使用单线程执行定时任务,因此所有的定时任务是串行执行的。

基于SchedulingConfigurer接口(动态调度,单线程,串行执行)

在基于基于@Scheduled注解(静态方式)中,每个一个定时任务都在系统启动前定义完成,启动之后无法修改。而基于SchedulingConfigurer接口的方式可以实现系统可在启动之后,从数据库中读取定时任务调度策略,实现动态调度定时任务。

包含以下操作:

  1. 配置环境
    引入maven依赖、配置数据库连接、插入cron表达式

  2. 实现SchedulingConfigurer接口,重写configureTasks,并使用@EnableScheduling注解开启定时任务

    @Configuration      //1.主要用于标记配置类,兼备Component的效果。
    @EnableScheduling   // 2.开启定时任务
    public class DynamicScheduleTask implements SchedulingConfigurer {/*** 执行定时任务.*/@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.addTriggerTask(//1.添加任务内容(Runnable)() -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),//2.设置执行周期(Trigger)triggerContext -> {//2.1 从数据库获取执行周期,实现动态获取定时任务调度逻辑String cron = cronMapper.getCron();//2.2 合法性校验.if (StringUtils.isEmpty(cron)) {// Omitted Code ..}//2.3 返回执行周期(Date)return new CronTrigger(cron).nextExecutionTime(triggerContext);});}
    }
    

同静态调度类似,由于使用单线程执行定时任务,因此所有的定时任务是串行执行的。

基于SchedulingConfigurer接口(动态调度,多线程,并行执行)

需要注意的是,SchedulingConfigurer 默认使用的也是单线程的方式,如果需要配置多线程,则需要指定线程池,有以下两种方式

  1. 在SchedulingConfigurer #configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar)方法中,使用ScheduledTaskRegistrar.setTaskScheduler(TaskScheduler taskScheduler)设置线程池。

    线程池使用ThreadPoolTaskScheduler类型。

    //配置类
    @Configuration
    @EnableScheduling
    public class AsyncTaskConfig implements SchedulingConfigurer{//线程池线程数量private int corePoolSize = 5;//线程池@Beanpublic ThreadPoolTaskScheduler taskScheduler(){ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.initialize();//初始化线程池scheduler.setPoolSize(corePoolSize);//线程池容量return scheduler;}@Overridepublic void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {//设置线程池scheduledTaskRegistrar.setTaskScheduler(taskScheduler());}
    }
    
     //定时任务@Componentpublic class ChooseCourseTask {private static final Logger LOGGER = LoggerFactory.getLogger(ChooseCourseTask.class);//定义任务调试策略@Scheduled(cron="0/3 * * * * *")//每隔3秒去执行public void task1(){LOGGER.info(Thread.currentThread().getName()+"===task run");LOGGER.info("===============测试定时任务1开始===============");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}LOGGER.info("===============测试定时任务1结束===============");}//定义任务调试策略@Scheduled(cron="0/3 * * * * *")//每隔3秒去执行public void task2(){LOGGER.info(Thread.currentThread().getName()+"===task run");LOGGER.info("===============测试定时任务2开始===============");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}LOGGER.info("===============测试定时任务2结束===============");}
    
     //输出结果2022-06-15 14:08:02.007 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1结束===============2022-06-15 14:08:02.007 [taskScheduler-8] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2结束===============2022-06-15 14:08:03.000 [taskScheduler-6] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-6===task run2022-06-15 14:08:03.000 [taskScheduler-6] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2开始===============2022-06-15 14:08:03.000 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-4===task run2022-06-15 14:08:03.000 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1开始===============2022-06-15 14:08:08.013 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1结束===============2022-06-15 14:08:08.013 [taskScheduler-6] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2结束===============2022-06-15 14:08:09.009 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-4===task run2022-06-15 14:08:09.009 [taskScheduler-7] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-7===task run2022-06-15 14:08:09.009 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1开始===============2022-06-15 14:08:09.009 [taskScheduler-7] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2开始===============
  2. @Async异步+线程池的两种方式

    这种方式需要实现AsyncConfigurer、SchedulingConfigurer接口,在实现类中配置线程池信息。同时在调度任务处使用@Async注解

    @Async注解可用在类或方法上。

     //配置类@Configuration@EnableSchedulingpublic class AsTest implements AsyncConfigurer {@Beanpublic ThreadPoolTaskScheduler taskScheduler(){ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.initialize();//初始化线程池scheduler.setPoolSize(10);//线程池容量return scheduler;}@Overridepublic Executor getAsyncExecutor() {Executor executor = taskScheduler();return executor;}
    
     // 定时任务@Componentpublic class ChooseCourseTask {private static final Logger LOGGER = LoggerFactory.getLogger(ChooseCourseTask.class);//定义任务调试策略@Scheduled(cron="0/3 * * * * *")//每隔3秒去执行@Asyncpublic void task1(){LOGGER.info(Thread.currentThread().getName()+"===task run");LOGGER.info("===============测试定时任务1开始===============");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}LOGGER.info("===============测试定时任务1结束===============");}//定义任务调试策略@Scheduled(cron="0/3 * * * * *")//每隔3秒去执行@Asyncpublic void task2(){LOGGER.info(Thread.currentThread().getName()+"===task run");LOGGER.info("===============测试定时任务2开始===============");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}LOGGER.info("===============测试定时任务2结束===============");}
    
     //输出结果2022-06-15 14:12:21.007 [taskScheduler-2] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-2===task run2022-06-15 14:12:21.007 [taskScheduler-1] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-1===task run2022-06-15 14:12:21.007 [taskScheduler-2] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2开始===============2022-06-15 14:12:21.007 [taskScheduler-1] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1开始===============2022-06-15 14:12:26.013 [taskScheduler-1] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1结束===============2022-06-15 14:12:26.013 [taskScheduler-2] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2结束===============2022-06-15 14:12:27.009 [taskScheduler-3] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-3===task run2022-06-15 14:12:27.009 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - taskScheduler-4===task run2022-06-15 14:12:27.009 [taskScheduler-3] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2开始===============2022-06-15 14:12:27.009 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1开始===============2022-06-15 14:12:32.011 [taskScheduler-4] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务1结束===============2022-06-15 14:12:32.011 [taskScheduler-3] INFO  c.guoj.order.mq.ChooseCourseTask - ===============测试定时任务2结束===============
    

这种方式通过配置线程池的方式执行定时任务,因此可以并行执行,最大并行度同多线程的数量相关。

实现原理

Spring Task定时任务的基本使用中,主要使用了@EnableScheduling@Scheduled,前者用户开启定时任务,后者用于标记定时任务的执行逻辑,即先开启后使用。因此先分析@EnableScheduling注解。

@EnableScheduling注解

该注解的代码源码很简单,其核心在于引入了一个SchedulingConfiguration.class配置类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class}) //导入配置类
@Documented
public @interface EnableScheduling {
}

SchedulingConfiguration.class配置类的源码也很简单,只是创建了一个ScheduledAnnotationBeanPostProcessor实例。从名字上看,该实例一种BeanPostProcessor(后置处理器)。

后置处理器作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。

@Configuration
@Role(2)
public class SchedulingConfiguration {public SchedulingConfiguration() {}@Bean(name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"})//创建一个ScheduledAnnotationBeanPostProcessor实例@Role(2)public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {return new ScheduledAnnotationBeanPostProcessor();}
}

综上,@EnableScheduling注解是通过创建一个ScheduledAnnotationBeanPostProcessor实例实现开启定时任务的。

ScheduledAnnotationBeanPostProcessor类

查看源码可知,该类实现一堆接口,此处不再逐个介绍这些接口,主要分析该类实现开启定时任务的实现逻辑。

public class ScheduledAnnotationBeanPostProcessor implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor, Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware, SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {....
}

postProcessAfterInitialization方法

该方法位于ScheduledAnnotationBeanPostProcessor类中,主要作用是找出全部被@Scheduled注解标记的方法,并调用processScheduled方法进行下一步处理。

public Object postProcessAfterInitialization(Object bean, String beanName) {Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);// 1 找出全部被`@Scheduled`注解标记的方法if (!this.nonAnnotatedClasses.contains(targetClass)) {Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);return !scheduledMethods.isEmpty() ? scheduledMethods : null;});// 2 如果类中没有使用 @Scheduled注解,则将其加入不再访问名单if (annotatedMethods.isEmpty()) {this.nonAnnotatedClasses.add(targetClass);if (this.logger.isTraceEnabled()) {this.logger.trace("No @Scheduled annotations found on bean class: " + bean.getClass());}} else {//3 该类中有方法被@Scheduled注解标注,则使用processScheduled处理该方法annotatedMethods.forEach((method, scheduledMethods) -> {scheduledMethods.forEach((scheduled) -> {this.processScheduled(scheduled, method, bean);});});if (this.logger.isDebugEnabled()) {this.logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods);}}}return bean;}

processScheduled方法

该方法位于ScheduledAnnotationBeanPostProcessor类中,该类主要作用是

  • 1)检测被@Scheduled注解是否有参数
  • 2)被@Scheduled注解标注的方法是否是无参且无返回值
  • 3)使用ScheduledTaskRegistrar注册定时任务,后加入任务列表
public class ScheduledAnnotationBeanPostProcessor implements xxx{//ScheduledTaskRegistrar这个类为Spring容器定时任务注册中心,并使用taskScheduler执行定时任务//默认情况下,使用单线程执行,可配置为多线程。private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();protected void processScheduled(Scheduled scheduled, Method method, Object bean) {try {//1 被@Scheduled注解标记的方法必须无参Assert.isTrue(method.getParameterCount() == 0, "Only no-arg methods may be annotated with @Scheduled");Method invocableMethod = AopUtils.selectInvocableMethod(method, bean.getClass());Runnable runnable = new ScheduledMethodRunnable(bean, invocableMethod);boolean processedSchedule = false;//2 被@Scheduled注解标记的方法使用 cron、fixedDelay、fixedRate三者之一String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";// 3 定义一个set  存储待调度的任务Set<ScheduledTask> tasks = new LinkedHashSet(4);long initialDelay = scheduled.initialDelay();String initialDelayString = scheduled.initialDelayString();//4.1  将cron类型的任务 放入setString cron = scheduled.cron();if (StringUtils.hasText(cron)) {tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));}//4.2 将fixedDelay类型的任务 放入setlong fixedDelay = scheduled.fixedDelay();if (fixedDelay >= 0L) {tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));}//4.3 将fixedDelayString类型的任务 放入setString fixedDelayString = scheduled.fixedDelayString();if (StringUtils.hasText(fixedDelayString)) {tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));}//4.4 将fixedRate类型的任务 放入setlong fixedRate = scheduled.fixedRate();if (fixedRate >= 0L) {tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));}//4.5 将fixedRateString类型的任务 放入setString fixedRateString = scheduled.fixedRateString();if (StringUtils.hasText(fixedRateString)) { tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay))); }//5 最后将 task 注册到 scheduledTasks 中((Set)registeredTasks).addAll(tasks);}}

ScheduledTaskRegistrar类

该类实现了三个接口,其中

  • InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。
  • DisposableBean接口和InitializingBean接口一样,为bean提供了释放资源方法的方式,它只包括destroy方法,凡是继承该接口的类,在bean被销毁之前都会执行该方法。
  • ScheduledTaskHolder接口定义返回当前实例的任务列表。
public class ScheduledTaskRegistrar implements ScheduledTaskHolder, InitializingBean, DisposableBean {@Nullableprivate TaskScheduler taskScheduler;@Nullableprivate ScheduledExecutorService localExecutor;@Nullableprivate List<TriggerTask> triggerTasks;@Nullableprivate List<CronTask> cronTasks;@Nullableprivate List<IntervalTask> fixedRateTasks;@Nullableprivate List<IntervalTask> fixedDelayTasks;private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<>(16);private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<>(16);/*** 设置调度器TaskScheduler实例*/public void setTaskScheduler(TaskScheduler taskScheduler) {Assert.notNull(taskScheduler, "TaskScheduler must not be null");this.taskScheduler = taskScheduler;}//set列表 其他set省略public void setFixedDelayTasksList(List<IntervalTask> fixedDelayTasks) {this.fixedDelayTasks = fixedDelayTasks;}//get列表 其他get省略public List<IntervalTask> getFixedDelayTaskList() {return (this.fixedDelayTasks != null ? Collections.unmodifiableList(this.fixedDelayTasks) :Collections.emptyList());}//add定时任务 其他省略public void addCronTask(CronTask task) {if (this.cronTasks == null) {this.cronTasks = new ArrayList<>();}this.cronTasks.add(task);}/***InitializingBean接口定义的方法* Calls {@link #scheduleTasks()} at bean construction time.*/@Overridepublic void afterPropertiesSet() {scheduleTasks();}/*** 执行定时任务* Schedule all registered tasks against the underlying* {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}.*/@SuppressWarnings("deprecation")protected void scheduleTasks() {if (this.taskScheduler == null) {//默认使用单线程调度器this.localExecutor = Executors.newSingleThreadScheduledExecutor();//真正调度的为 ConcurrentTaskScheduler实例this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);}//添加四种执行调度 此处只留一个 其他省略if (this.triggerTasks != null) {for (TriggerTask task : this.triggerTasks) {addScheduledTask(scheduleTriggerTask(task));}}/*** 指定四种任务  此处只留一个 其他省略* Schedule the specified cron task, either right away if possible* or on initialization of the scheduler.* @return a handle to the scheduled task, allowing to cancel it* (or {@code null} if processing a previously registered task)* @since 4.3*/@Nullablepublic ScheduledTask scheduleCronTask(CronTask task) {ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);boolean newTask = false;if (scheduledTask == null) {scheduledTask = new ScheduledTask(task);newTask = true;}if (this.taskScheduler != null) {//执行任务scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());}else {addCronTask(task);this.unresolvedTasks.put(task, scheduledTask);}return (newTask ? scheduledTask : null);}/*** Schedule the specified fixed-rate task, either right away if possible* or on initialization of the scheduler.* @return a handle to the scheduled task, allowing to cancel it* (or {@code null} if processing a previously registered task)* @since 4.3* @deprecated as of 5.0.2, in favor of {@link #scheduleFixedRateTask(FixedRateTask)}*/@Deprecated@Nullablepublic ScheduledTask scheduleFixedRateTask(IntervalTask task) {FixedRateTask taskToUse = (task instanceof FixedRateTask ? (FixedRateTask) task :new FixedRateTask(task.getRunnable(), task.getInterval(), task.getInitialDelay()));return scheduleFixedRateTask(taskToUse);}/*** ScheduledTaskHolder接口的唯一的方法 返回ScheduledTask集合* Return all locally registered tasks that have been scheduled by this registrar.* @since 5.0.2* @see #addTriggerTask* @see #addCronTask* @see #addFixedRateTask* @see #addFixedDelayTask*/@Overridepublic Set<ScheduledTask> getScheduledTasks() {return Collections.unmodifiableSet(this.scheduledTasks);}//DisposableBean接口的唯一的方法 销毁任务、收回线程池资源@Overridepublic void destroy() {for (ScheduledTask task : this.scheduledTasks) {task.cancel();}if (this.localExecutor != null) {this.localExecutor.shutdownNow();}}}

总结

  1. spring启动过程中读取到EnableScheduling注解,然后执行解析封装等操作,解析完成之后,就创建了ScheduledAnnotationBeanPostProcessor实例
  2. ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization()方法拦截被@Scheduled修饰方法
  3. ScheduledAnnotationBeanPostProcessor#processScheduled()方法解析@Scheduled注解的参数,加入任务列表,注册定时任务
  4. ScheduledTaskRegistrar在spring初始化Bean时触发定时任务的执行,具体执行由ConcurrentTaskScheduler实例完成

参考资料

  • SpringBoot中@Scheduled实现多线程并发定时任务 - 简书 (jianshu.com)
  • 玩转SpringBoot之定时任务详解 - 淼淼之森 - 博客园 (cnblogs.com)
  • Spring 定时任务与原理解析 - 简书 (jianshu.com)
  • Spring之BeanPostProcessor(后置处理器)介绍 - 云+社区 - 腾讯云 (tencent.com)
  • Spring @Scheduled执行原理解析 - kaleidoscopic - 博客园 (cnblogs.com)
  • @Scheduled定时_静若繁花_jingjing的博客-CSDN博客_@scheduled注解配置时间
  • 任务调度处理系列之 Spring源码分析-【SchedulingConfigurer实现原理】_Coder_Boy_的博客-CSDN博客_scheduledtaskregistrar如何初始化

基于Spring Task的定时任务实现及原理分析相关推荐

  1. 使用Spring Task完成定时任务

    1. 前言 上一篇我们学习了Quartz作为定时任务的框架的使用, 这一篇我们来学习Spring全家桶的SpringTask, 对于主张简单易用的Spring家族来说, SpringTask无疑也是一 ...

  2. 使用Spring Task实现定时任务

    文章目录 SpringMVC 配置方式 添加命名空间 配置Task注解扫描 Springboot配置方式 定义定时任务 异常处理 项目中实现定时任务有多种方式,除了TimerTask这种小工具之外,以 ...

  3. spring源码阅读--aop实现原理分析

    aop实现原理简介 首先我们都知道aop的基本原理就是动态代理思想,在设计模式之代理模式中有介绍过这两种动态代理的使用与基本原理,再次不再叙述. 这里分析的是,在spring中是如何基于动态代理的思想 ...

  4. 实战:Spring Boot源码解读与原理分析

    承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解读与原理剖析> ...

  5. Spring Cloud Gateway 过滤器执行顺序原理分析

    过滤器类型 GlobalFilter:全局过滤器,对所有路由生效.通过实现GlobalFilter接口创建 GatewayFilter:网关过滤器,也可以说是局部过滤器.自定义过滤器,只对配置了此过滤 ...

  6. Spring事务管理 | 数据库连接池流程原理分析

  7. Spring 使用介绍(十二)—— Spring Task

    一.概述 1.jdk的线程池和任务调用器分别由ExecutorService.ScheduledExecutorService定义,继承关系如下: ThreadPoolExecutor:Executo ...

  8. 基于pxe技术实现linux自动安装原理,网络安装Linux的技术原理分析及实现

    网络安装Linux的技术原理分析及实现 李怀刚;邱建新 [期刊名称]<计算机应用与软件> [年(卷),期]2006(023)009 [摘要]对自动化Linux网络安装所使用的相关技术原理进 ...

  9. 图像金字塔LK光流法原理分析

    图像金字塔LK光流法原理分析 1.LK光流法原理分析 2.基于图像金字塔的LK光流法原理分析 本篇博客只讲述原理,c++代码实现请参考博客< 基于金字塔LK的光流法实现-根据论文自己实现的c++ ...

  10. Spring Task定时任务的配置和使用详解

    spring中使用定时任务 1.基于xml配置文件使用定时任务 首先配置spring开启定时任务 <beans xmlns="http://www.springframework.or ...

最新文章

  1. ios签名软件_如何给无法上架App Store的App做ios签名?
  2. R语言文件下载:谁来帮我把这个128个音频下载一下
  3. python怎么把数据写入txt-python(如何将数据写入本地txt文本文件)
  4. Linux 下源码编译安装 vim 8.1
  5. 图解HTTP学习笔记
  6. pandas: DataFrame在数据处理时一些常用的操作汇总
  7. 【leetcode】Search for a Range
  8. Maven 系列 2:Maven 本地仓库与远程仓库配置完整步骤以及修改 settings.xml 后的完整内容(配置非私服,远程仓储镜像强力推荐阿里云)
  9. 兰州中考计算机考试,宜昌、兰州发布中考新政新消息:增加口语考试,采取人机对话形式...
  10. 计算机表格入门2013,Access2013从入门到精通
  11. python画k线_python下画k线
  12. SIF协议(一线通)
  13. 卤煮花生米的制作过程(高压锅版)
  14. rk3399 android 9.0 root 权限及测试应用
  15. ChucK初步(5)
  16. Win7——无Internet访问权限
  17. html中点重置和提交没反应,为什么点击按钮没反应??
  18. React中的SVG陷阱
  19. python能否取代excel_行,Python玩大了!​取代Excel,程序员:太牛!你怎么看?...
  20. Node.JS基础知识之命令行窗口(Windows 的命令行窗口)

热门文章

  1. Sprint 增量 vs 潜在可交付产品 vs MVP vs MMP
  2. 关于比特币的一些问题
  3. php网上商城 站长之家,php网上商城购物车设计代码分享
  4. Vue2.0图片上传及图片压缩自定义H5图片上传组件
  5. Kotlin中的 Elvis 运算符 ?:
  6. 词嵌入|深度学习(李宏毅)(七)
  7. vue项目中使用ElementUI与第三方图标库Iconfont(亲测有效)
  8. OpenVINS 代码解读(2) - Feature特征点相关
  9. 你也对阅读源码感兴趣,说说我是如何阅读Nacos源码的
  10. web开发环节,web安全基础教程