Jan 10, 2018
Jun 8, 2023
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

SpringのTaskSchedulerのうち、もっともよく使われるorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutorの設定ミスが多いです。

  • corePoolSize
  • maxPoolSize
  • queueCapacity

が設定項目としてあります。

これだけ見ると、このExecutorTheadを作る順としては

  1. corePoolSizeThreadを最初に作る
  2. corePoolSizeが一杯になるとmaxPoolSizeまでThreadを増やす
  3. maxPoolSizeを越えると、queueCapacityまでキューイングする
  4. queueCapacityを越えるとrejectされる

と思いがちです。

しかし、これは誤解です。

正しくは

  1. corePoolSizeまでThreadを作る
  2. corePoolSizeが一杯になるとqueueCapacityまでキューイングする
  3. queueCapacityを越えるとmaxPoolSizeまでThreadを増やす
  4. maxPoolSizeを越えるとrejectされる

です。

corePoolSizeのデフォルト値は1maxPoolSize,queueCapacity共にデフォルト値はInteger.MAX_VALUEです。

次のような設定は意味がありません。

@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(4);
    taskExecutor.setMaxPoolSize(40);
    return taskExecutor;
}

queueCapacityを設定していないので、最大スレッド数は4です。それ以上のリクエストは全てキューイングされます。40という数字に意味がありません。

@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setMaxPoolSize(40);
    return taskExecutor;
}

corePoolSizequeueCapacityを設定していないので、最大スレッド数は1です。それ以上のリクエストは全てキューイングされます。

スレッド数を4-40に設定したいのであれば、例えば次のように設定する必要があります。(ただし、初回に4スレッドできる訳ではありません)

@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(4);
    taskExecutor.setQueueCapacity(4);
    taskExecutor.setMaxPoolSize(40);
    return taskExecutor;
}

設定値を次の順番で認識しておいたほうが間違いにくいです。

  • corePoolSize
  • queueCapacity
  • maxPoolSize

あるいは最大スレッド数が40でることのみ指定したい場合は

@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(40);
    return taskExecutor;
}

でOKです。

ちなみにThreadPoolTaskExecutorが委譲しているjava.util.concurrent.ThreadPoolExecutorを作るときは次のような使い方をすることが多いと思います。

ExecutorService executorService = Executors.newFixedThreadPool(40);

これは

@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(40);
    taskExecutor.setMaxPoolSize(40);
    return taskExecutor;
}

に近いです。

Spring Bootを使う場合は、上記のようにThreadPoolTaskExecutorを定義するよりもAutoConfigurationを使用すべきです。

https://docs.spring.io/spring-boot/reference/features/task-execution-and-scheduling.html#page-title

この場合は、次のプロパティを使ってPool sizeを設定できます。

spring.task.execution.pool.core-size=4
spring.task.execution.pool.queue-capacity=4
spring.task.execution.pool.max-size=40

直感的にThreadを増やすことを先に行なって欲しい場合は以下のriptide-concurrentの使用を検討してみてください。

https://github.com/zalando/riptide/tree/main/riptide-concurrent

Found a mistake? Update the entry.
Share this article: