web-dev-qa-db-ja.com

FixedThreadPool対CachedThreadPool:2つの悪の小さい方

多数のタスクを実行するスレッド(約5〜150)を生成するプログラムがあります。もともと、私はFixedThreadPoolを使用していました。これは この同様の質問 がより長寿命のタスクに適していることを示唆し、マルチスレッドの知識が非常に限られているため、スレッドの平均寿命(数分) "長命)。

ただし、最近、追加のスレッドを生成する機能を追加しました。これにより、設定したスレッド制限を超えてしまいます。この場合、許可できるスレッド数を推測して増やしたり、CachedThreadPoolに切り替えて無駄なスレッドをなくした方が良いでしょうか?

両方を試してみて、違いはありませんseemはありませんので、避けるためにCachedThreadPool無駄。ただし、スレッドの寿命は、代わりにFixedThreadPoolを選択し、未使用のスレッドを処理する必要があることを意味しますか? この質問 は、これらの余分なスレッドが無駄にされていないように見えますが、説明をいただければ幸いです。

83
Daniel

CachedThreadPoolは、長時間実行されるスレッドに使用しても悪影響はないため、状況に合わせて使用​​する必要があります。 CachedThreadPoolsが短いタスクに適しているというJava docのコメントは、それらがそのような場合に特に適していることを示唆しているだけで、長時間実行されるタスクを含むタスクには使用できない、または使用すべきではありません。

さらに詳しく説明すると、 Executors.newCachedThreadPoolExecutors.newFixedThreadPool は両方とも、同じスレッドプール実装(少なくともオープンJDKでは)によって、異なるパラメーターを使用してサポートされています。違いは、スレッドの最小値、最大値、スレッドのキル時間、およびキューのタイプだけです。

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

FixedThreadPoolには、実際に固定数のスレッドで作業する場合に利点があります。これは、指定したレベルでスレッド数が維持されることを認識しながら、任意の数のタスクをexecutorサービスに送信できるためです。スレッドの数を明示的に増やしたい場合、これは適切な選択ではありません。

ただし、これは、CachedThreadPoolで発生する可能性がある1つの問題が、同時に実行されるスレッドの数を制限することに関することを意味します。 CachedThreadPoolはそれらを制限しません。そのため、あまり多くのスレッドを実行しないように、独自のコードを記述する必要がある場合があります。これは、アプリケーションの設計と、executorサービスへのタスクの送信方法に大きく依存します。

100
Trevor Freeman

FixedThreadPoolCachedThreadPoolの両方は、負荷の高いアプリケーションでは悪です。

CachedThreadPoolFixedThreadPoolよりも危険です

アプリケーションの負荷が高く、低レイテンシが必要な場合は、以下の欠点があるため、両方のオプションを取り除く方が良い

  1. タスクキューの制限のない性質:メモリ不足または高レイテンシを引き起こす可能性があります
  2. 実行時間が長いスレッドは、スレッド作成時にCachedThreadPoolの制御を失います。

両方が悪であることを知っているので、より小さい悪は良いことをしません。 ThreadPoolExecutor を優先します。これにより、多くのパラメーターをきめ細かく制御できます。

  1. より適切に制御するために、タスクキューを境界キューとして設定します
  2. 適切なRejectionHandlerを持つ-JDKが提供する独自のRejectionHandlerまたはDefaultハンドラー
  3. タスクの完了前/完了後に何かすることがある場合は、beforeExecute(Thread, Runnable)afterExecute(Runnable, Throwable)をオーバーライドします
  4. オーバーライド ThreadFactory スレッドのカスタマイズが必要な場合
  5. 実行時にスレッドプールサイズを動的に制御する(関連するSEの質問: 動的スレッドプール
36
Ravindra babu

だから、たくさんのタスクを実行するスレッド(〜5-150)を生成するプログラムがあります。

選択したOSおよびハードウェアによってスレッドが実際に処理される方法を理解していますか? JavaがスレッドをOSスレッドにマップする方法、スレッドをCPUスレッドにマップする方法などONE JREで150個のスレッドを作成することは、その下に大規模なCPUコア/スレッドがある場合にのみ意味があるため、ほとんどの場合そうではないからです。 OSおよび使用中のRAMによっては、n個を超えるスレッドを作成すると、OOMエラーが原因でJREが終了することさえあります。したがって、スレッドとそれらのスレッドが行う作業、処理できる作業の数などを実際に区別する必要があります。

それがCachedThreadPoolの問題です。これらのスレッドを処理できるCPUコアが2つしかないため、実際には実行できないスレッドで長時間実行される作業をキューに入れるのは意味がありません。 150のスケジュールされたスレッドで終わる場合、Java内で使用されるスケジューラーとOSがそれらを同時に処理するために多くの不必要なオーバーヘッドを作成する可能性があります。これは、スレッドが常にI/Oなどを待機していない限り、CPUコアが2つしかない場合は不可能です。しかし、その場合でも、多くのスレッドは多くのI/Oを作成します...

そして、その問題は、例えばで作成されたFixedThreadPoolでは発生しません。 2 + nスレッド。nはもちろん妥当な低さです。ハードウェアとOSのリソースは、とにかく実行できないスレッドを管理するためのオーバーヘッドがはるかに少ないためです。

5