• 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
  • 相关阅读:
    【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]
    【BZOJ1926】【SDOI2010】粟粟的书架 [主席树]
    【BZOJ4069】【APIO2015】巴厘岛的雕塑 [贪心][DP]
    【BZOJ2815】【ZJOI2012】灾难 [LCA]
    【BZOJ3453】XLkxc [拉格朗日插值法]
    【BZOJ2850】巧克力王国 [KD-tree]
    《类和对象》
    @RequestMapping映射请求
    010-Python-socket编程
    SSH-Struts第二弹:一个Form提交两个Action
  • 原文地址:https://www.cnblogs.com/jing-yi/p/13121745.html
Copyright © 2020-2023  润新知