IK.AM

@making's tech note


誤解しがちなThreadPoolTaskExecutorの設定

🗃 {Programming/Java/org/springframework/scheduling/concurrent}
🏷 Java 🏷 Spring 
🗓 Updated at 2023-06-08T05:28:32Z  🗓 Created at 2018-01-10T02:35:56Z   🌎 English Page

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;
}

に近いです。


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


✒️️ Edit  ⏰ History  🗑 Delete