Spring Boot 定時調度竟然只用單線程?!

爲防止打臉,先寫明版本:spring boot 2.2.2.RELEASEjava

spring boot 中自帶了一個輕量級的任務調度框架,使用也很是簡單。spring

  1. 添加註解@EnableScheduling,固然須要放在一個能夠被掃描到的類上,好比啓動類、使用了@Configuration的配置類。固然你要放在一個@Component的類上除了不太規範,我也無話可說。
  2. 在須要定時運行的方法上加上@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.

整理下,大概意思是,默認狀況下,會尋找一個調度器,按照如下順序

  1. 惟一的org.springframework.scheduling.TaskScheduler類型的bean
  2. 一個名稱爲taskSchedulerorg.springframework.scheduling.TaskScheduler類型的bean
  3. 惟一的java.util.concurrent.ScheduledExecutorService類型的bean
  4. 一個名稱爲taskSchedulerjava.util.concurrent.ScheduledExecutorService類型的bean
  5. 建立一個單線程的調度器

若是想掌控更多,能夠寫一個實現了SchedulingConfigurer接口的配置類。若是在這個配置類設置了調度器,就不會再尋找了

看上去應該是走到了第5種狀況。

@EnableScheduling引入了SchedulingConfigurationSchedulingConfiguration中定義了一個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。

看一下上面的條件

  1. 有名稱爲org.springframework.context.annotation.internalScheduledAnnotationProcessor的bean才加載,而這個名稱就是SchedulingConfiguration中定義ScheduledAnnotationBeanPostProcessor的名稱。
  2. 當缺乏SchedulingConfigurerTaskSchedulerScheduledExecutorService這些類型的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 自動配置的,你也能夠本身定義,只要搞清楚尋找的優先級就沒問題了。

看到了這裏必定是真愛了,關注微信公衆號【憨憨的春天】第一時間獲取更新
qrcode_for_gh_7fff61e23381_344.jpg

相關文章
相關標籤/搜索