spring定时任务原理

参考文章: https://juejin.im/post/5b64448af265da0f7f44c201

                   https://juejin.im/post/5e338ebae51d4558864b1ca0

1、开发中使用时要注意的点

  (0)spring定时任务执行原理实际使用的是 JDK 自带的 ScheduledExecutorService

  (1)spring默认使用单线程的线程池去执行定时任务,所以如果某个任务执行时间过长,会导致其他定时任务阻塞无法执行。

  (2)可以开启并行调度,springboot中的使用方式:这种模式每次任务执行都会创建一个线程去执行。

@EnableAsync
@EnableScheduling
@SpringBootApplication
public class QuickMediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuickMediaApplication.class, args);
    }

    @Scheduled(cron = "0/1 * * * * ?")
    @Async
    public void sc1()  {
        System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis());
    }
}

  风险:如果某个定时任务出现死循环或者执行时间过长而出发时间较短,会导致线程数量不可控。

  (3)最稳妥的处理方式:自定义任务执行的线程池,如下:

  普通spring配置:

@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setThreadNamePrefix("task-schedule-");
    executor.setMaxPoolSize(10);
    executor.setCorePoolSize(3);
    executor.setQueueCapacity(0);
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    return executor;
}

或

@Bean
public ScheduleExecutorService scheduleExecutorService{
    return Executors.new ScheduleThreadPool(10);    
}

  springboot中配置:

spring.task.scheduling.pool.size=10
spring.task.scheduling.thread-name-prefix=task-schedule-

2、源码分析

  通过监听IOC容器初始化事件,扫描所有 Bean 中带有 @Scheduled 注解的方法,然后封装成 Task 子类放置到 ScheduledTaskRegistrar中。如果自己定义了ScheduledExecutorService,会使用自己定义的线程池,否则ScheduledTaskRegistrar#afterPropertiesSet 创建一个单线程的定时任务执行器 ScheduledExecutorService,注入到 ConcurrentTaskScheduler中,然后通过 taskScheduler 执行定时任务。

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

  ................ @Override
public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext() == this.applicationContext) { // Running in an ApplicationContext -> register tasks this late... // giving other ContextRefreshedEvent listeners a chance to perform // their work at the same time (e.g. Spring Batch's job registration). finishRegistration(); } } private void finishRegistration() { if (this.scheduler != null) { this.registrar.setScheduler(this.scheduler); } .......................if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) { Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type"); try { // Search for TaskScheduler bean... this.registrar.setTaskScheduler(resolveSchedulerBean(beanFactory, TaskScheduler.class, false)); } catch (NoUniqueBeanDefinitionException ex) { logger.debug("Could not find unique TaskScheduler bean", ex); try { this.registrar.setTaskScheduler(resolveSchedulerBean(beanFactory, TaskScheduler.class, true)); } catch (NoSuchBeanDefinitionException ex2) { if (logger.isInfoEnabled()) { logger.info("More than one TaskScheduler bean exists within the context, and " + "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " + "(possibly as an alias); or implement the SchedulingConfigurer interface and call " + "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " + ex.getBeanNamesFound()); } } } catch (NoSuchBeanDefinitionException ex) { logger.debug("Could not find default TaskScheduler bean", ex); // Search for ScheduledExecutorService bean next... try { this.registrar.setScheduler(resolveSchedulerBean(beanFactory, ScheduledExecutorService.class, false)); } catch (NoUniqueBeanDefinitionException ex2) { logger.debug("Could not find unique ScheduledExecutorService bean", ex2); try { this.registrar.setScheduler(resolveSchedulerBean(beanFactory, ScheduledExecutorService.class, true)); } catch (NoSuchBeanDefinitionException ex3) { if (logger.isInfoEnabled()) { logger.info("More than one ScheduledExecutorService bean exists within the context, and " + "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " + "(possibly as an alias); or implement the SchedulingConfigurer interface and call " + "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " + ex2.getBeanNamesFound()); } } } catch (NoSuchBeanDefinitionException ex2) { logger.debug("Could not find default ScheduledExecutorService bean", ex2); // Giving up -> falling back to default scheduler within the registrar... logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing"); } } } this.registrar.afterPropertiesSet(); } }
if (this.taskScheduler == null) {
      this.localExecutor = Executors.newSingleThreadScheduledExecutor();
      this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}

 

  1. Spring 定时任务执行原理实际使用的是 JDK 自带的 ScheduledExecutorService
posted @ 2020-06-13 20:48  jingyi_up  阅读(1064)  评论(0编辑  收藏  举报