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); }
Spring
定时任务执行原理实际使用的是JDK
自带的ScheduledExecutorService