TIL: Scheduled(ThreadPool)Executor has no thread size upper bound

(2020 年代に Java の話ですまぬ仕事なので・・・)

表題通り。ScheduledThreadPoolExecutor はスレッドプールのスレッド数の上限を指定することができない。そしてこれが Java 標準で入っている唯一のスレッドプール対応 ScheduledExecutorService である。"Scheduled" というのは「何ミリ秒待ってから実行してね」みたいな API があるということです。

これ何が困るかというと、たとえばアプリの初期化とかで重いタスクをポイポイっと投入すると、投入した数だけスレッドができて並列に動いてしまうわけです。やめてくれ!クリティカルパスの CPU サイクルが奪われちゃうじゃないか!

なおスレッド数の上限を指定できる ThreadPoolExecutor という実装もあるが、これは "scheduled" な API が存在しない。困る。みんなどうしてるのかな・・・とおもって検索したら社内では欲しいものが誰かによって実装されていた。が、残念ながら Guava には入っていなかった。

"Scheduled" とかいらなくね?という気もするが、残念なことに Kotlin interop においては必須である。なぜなら Executor.asCoroutineDispatcher() で作った CoroutineDispatcher は、その Executor が scheduled でないと schedule の必要な操作 (delay など) をしたときサラっと内部の executor に fallback するという恐しい実装になっているからである。やめろ!!! delay() のあとはもとの executor に戻ってくるのかもしれないが(というか常識的に考えてそうなっているはずだがコードをパッと見た感じでは怪しいような)・・・

まあ適当になんとかしますが、みなそれぞれに出来が悪い。


なおいちおう挙動を確認してみたところ じっさいに fallback 先の DefaultExecutor でタスクが実行されることは確認できたが思わぬ行動もあり、自分が Kotlin coroutine continuation の実装を理解していないことがわかりました。しらね・・・