Spring Boot 定时调度居然只用单线程?!

为防止打脸,先写明版本:spring boot 2.2.2.RELEASE

spring boot 中自带了一个轻量级的任务调度框架,使用也非常简单。

添加注解@EnableScheduling,当然需要放在一个可以被扫描到的类上,比如启动类、使用了@Configuration的配置类。当然你要放在一个@Component的类上除了不太规范,我也无话可说。

在需要定时运行的方法上加上@Scheduled注解,并设置调度方式,支持

*   cron  
*   fixedDelay
*   fixedRate

就这么简单。

现在有两个任务A和B

任务A在5点执行,并耗时2个小时

@Scheduled(cron = "0 0 5 * * *")
public void taskA() throws InterruptedException {
    log.info("taskA running");
    Thread.sleep(2 * 60 * 60 * 1000);//模拟任务耗时,2个小时
}

任务B在6点执行

@Scheduled(cron = "0 0 6 * * *")
public void taskB() {
    log.info("taskB running");
}

灵魂一问:任务B能按预期在6点执行吗?

如果觉得能正常执行,怕是已经忘记了标题。

先说结论:任务B不能在6点执行,因为调度器是的线程池大小为1。

不用想,肯定是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 自动配置的,你也可以自己定义,只要搞清楚寻找的优先级就没问题了。

~阅读全文-人机检测~

微信公众号“Java精选”(w_z90110),专注Java技术干货分享!让你从此路人变大神!回复关键词领取资料:如Mysql、Hadoop、Dubbo、Spring Boot等,免费领取视频教程、资料文档和项目源码。微信搜索小程序“Java精选面试题”,内涵3000+道Java面试题!

涵盖:互联网那些事、算法与数据结构、SpringMVC、Spring boot、Spring Cloud、ElasticSearch、Linux、Mysql、Oracle等

评论

分享:

支付宝

微信