参考文章:https://juejin.im/post/5b64448af265da0f7f44c201
https://juejin.im/post/5e338ebae51d4558864b1ca0
1、开发中使用时要注意的点
(0)spring定时任务执行原理实际使用的是JDK
自带的ScheduledExecutorService
(1)spring默认使用单线程的线程池去执行定时任务,所以如果某个任务执行时间过长,会导致其他定时任务阻塞无法执行。
(2)可以开启并行调度,springboot中的使用方式:这种模式每次任务执行都会创建一个线程去执行。
@EnableAsync @EnableScheduling @SpringBootApplication public classQuickMediaApplication { public static voidmain(String[] args) { SpringApplication.run(QuickMediaApplication.class, args); } @Scheduled(cron = "0/1 * * * * ?") @Async public voidsc1() { System.out.println(Thread.currentThread().getName() + " | sc1 " +System.currentTimeMillis()); } }
风险:如果某个定时任务出现死循环或者执行时间过长而出发时间较短,会导致线程数量不可控。
(3)最稳妥的处理方式:自定义任务执行的线程池,如下:
普通spring配置:
@Bean publicAsyncTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor executor = newThreadPoolTaskExecutor(); executor.setThreadNamePrefix("task-schedule-"); executor.setMaxPoolSize(10); executor.setCorePoolSize(3); executor.setQueueCapacity(0); executor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy()); returnexecutor; } 或 @Bean publicScheduleExecutorService scheduleExecutorService{ return Executors.new ScheduleThreadPool(10); }
springboot中配置:
spring.task.scheduling.pool.size=10spring.task.scheduling.thread-name-prefix=task-schedule-
2、源码分析
通过监听IOC容器初始化事件,扫描所有Bean
中带有@Scheduled
注解的方法,然后封装成Task
子类放置到ScheduledTaskRegistrar中。如果自己定义了ScheduledExecutorService,会使用自己定义的线程池,否则
ScheduledTaskRegistrar#afterPropertiesSet
创建一个单线程的定时任务执行器 ScheduledExecutorService
,注入到 ConcurrentTaskScheduler
中,然后通过 taskScheduler
执行定时任务。
public classScheduledAnnotationBeanPostProcessor implementsScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor, Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware, SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
................ @Override public voidonApplicationEvent(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 voidfinishRegistration() { 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