springboot定时调度竟然只支持单线程?
为防止打脸,先写明版本:spring boot 2.2.2.RELEASEjava
springboot中自带了一个轻量级任务调度框架,使用也很是简单
- 添加注解
@EnableScheduling
,固然须要放在一个能够被扫描到的类上,好比启动类、使用了@Configuration
的配置类。固然你要放在一个@Component
的类上除了不太规范,我也无话可说。 在须要定时运行的方法上加上
@Scheduled
注解,并设置调度方式,支持微信- cron
- fixedDelay
- fixedRate
如今有两个任务A和Bide
任务A在5点执行,并耗时2个小时ui
@Scheduled(cron = "0 0 5 * * *")
public void taskA() throws InterruptedException {
log.info("taskA running");
Thread.sleep(2 * 60 * 60 * 1000);//模拟任务耗时,2个小时
}
任务B在6点执行spa
@Scheduled(cron = "0 0 6 * * *")
public void taskB() {
log.info("taskB running");
}
灵魂一问:任务B能按预期在6点执行吗?线程
若是以为能正常执行,怕是已经忘记了标题。code
三
先说结论:任务B不能在6点执行,由于调度器是的线程池大小为1。orm
不用想,确定是spring boot 自动配置的。
先看注解@EnableScheduling
,注释中有写到
By default, will be searching for an associated scheduler definition: either a unique org.springframework.scheduling.TaskScheduler bean in the context, or a TaskScheduler bean named "taskScheduler" otherwise; the same lookup will also be performed for a java.util.concurrent.ScheduledExecutorService bean. If neither of the two is resolvable, a local single-threaded default scheduler will be created and used within the registrar. When more control is desired, a @Configuration class may implement SchedulingConfigurer. This allows access to the underlying ScheduledTaskRegistrar instance.
整理下,大概意思是,默认状况下,会寻找一个调度器,按照如下顺序
- 惟一的
org.springframework.scheduling.TaskScheduler
类型的bean - 一个名称为
taskScheduler
的org.springframework.scheduling.TaskScheduler
类型的bean - 惟一的
java.util.concurrent.ScheduledExecutorService
类型的bean - 一个名称为
taskScheduler
的java.util.concurrent.ScheduledExecutorService
类型的bean - 建立一个单线程的调度器
若是想掌控更多,能够写一个实现了SchedulingConfigurer
接口的配置类。若是在这个配置类设置了调度器,就不会再寻找了。
看上去应该是走到了第5种状况。
四
@EnableScheduling
引入了SchedulingConfiguration
,SchedulingConfiguration
中定义了一个ScheduledAnnotationBeanPostProcessor
类型的bean。
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
设置调度器的代码在finishRegistration
方法中。
跟代码发现实际上是在执行第一种状况时就已经知足了。
这个bean是那里定义的?
五
一(全)番(凭)摸(运)索(气),找到了一个自动配置类TaskSchedulingAutoConfiguration
,其中定义了ThreadPoolTaskScheduler
类型的bean。
看一下上面的条件
- 有名称为
org.springframework.context.annotation.internalScheduledAnnotationProcessor
的bean才加载,而这个名称就是SchedulingConfiguration
中定义ScheduledAnnotationBeanPostProcessor
的名称。 - 当缺乏
SchedulingConfigurer
、TaskScheduler
、ScheduledExecutorService
这些类型的bean时才加载。这也是为了保持扩展性。开发者定义了相关的bean,框架就不自动配置了。
@Bean
@ConditionalOnBean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@ConditionalOnMissingBean({SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class})
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
return builder.build();
}
六
那么如何修改调度器线程池大小呢
在TaskSchedulingAutoConfiguration
类中定义了上面方法须要的TaskSchedulerBuilder
@Bean
@ConditionalOnMissingBean
public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties, ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
builder = builder.poolSize(properties.getPool().getSize());
Shutdown shutdown = properties.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
builder = builder.customizers(taskSchedulerCustomizers);
return builder;
}
能够看到线程池大小是经过读配置设置的,也就是设置spring.task.scheduling.pool.size
。
固然上述方法是沿用spring boot 自动配置的,你也能够本身定义,只要搞清楚寻找的优先级就没问题了。