--- title: 誤解しがちなThreadPoolTaskExecutorの設定 tags: ["Java", "Spring"] categories: ["Programming", "Java", "org", "springframework", "scheduling", "concurrent"] date: 2018-01-10T02:35:56Z updated: 2023-06-08T05:28:32Z --- Springの[`TaskScheduler`](https://docs.spring.io/autorepo/docs/spring-framework/5.0.x/spring-framework-reference/integration.html#scheduling)のうち、もっともよく使われる`org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor`の設定ミスが多いです。 * `corePoolSize` * `maxPoolSize` * `queueCapacity` が設定項目としてあります。 これだけ見ると、この`Executor`が`Thead`を作る順としては 1. `corePoolSize`を`Thread`を最初に作る 2. `corePoolSize`が一杯になると`maxPoolSize`まで`Thread`を増やす 3. `maxPoolSize`を越えると、`queueCapacity`までキューイングする 4. `queueCapacity`を越えるとrejectされる と思いがちです。 しかし、これは誤解です。 正しくは 1. `corePoolSize`まで`Thread`を作る 2. `corePoolSize`が一杯になると`queueCapacity`までキューイングする 3. `queueCapacity`を越えると`maxPoolSize`まで`Thread`を増やす 4. `maxPoolSize`を越えるとrejectされる です。 `corePoolSize`のデフォルト値は`1`、`maxPoolSize`,`queueCapacity`共にデフォルト値は`Integer.MAX_VALUE`です。 次のような設定は意味がありません。 ``` java @Bean ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(4); taskExecutor.setMaxPoolSize(40); return taskExecutor; } ``` `queueCapacity`を設定していないので、最大スレッド数は`4`です。それ以上のリクエストは全てキューイングされます。`40`という数字に意味がありません。 ``` java @Bean ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setMaxPoolSize(40); return taskExecutor; } ``` `corePoolSize`も`queueCapacity`を設定していないので、最大スレッド数は`1`です。それ以上のリクエストは全てキューイングされます。 スレッド数を`4`-`40`に設定したいのであれば、例えば次のように設定する必要があります。(ただし、初回に4スレッドできる訳ではありません) ``` java @Bean ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(4); taskExecutor.setQueueCapacity(4); taskExecutor.setMaxPoolSize(40); return taskExecutor; } ``` 設定値を次の順番で認識しておいたほうが間違いにくいです。 * `corePoolSize` * `queueCapacity` * `maxPoolSize` あるいは最大スレッド数が`40`でることのみ指定したい場合は ``` java @Bean ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(40); return taskExecutor; } ``` でOKです。 ちなみに`ThreadPoolTaskExecutor`が委譲している`java.util.concurrent.ThreadPoolExecutor`を作るときは次のような使い方をすることが多いと思います。 ``` java ExecutorService executorService = Executors.newFixedThreadPool(40); ``` これは ``` java @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を設定できます。 ```properties 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