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


93

たくさんのタスクを実行するスレッド(約5〜150)を生成するプログラムがあります。もともと、私が使用しFixedThreadPoolているため、この類似した質問は、彼らがより良い、より長い寿命のタスクに適した提案とマルチスレッドの私の非常に限られた知識で、私は、スレッド(数分)の平均寿命は「とみなさ長く住んでいました」。

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

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

回答:


108

CachedThreadPoolは、実行時間の長いスレッドに使用してもマイナスの影響がないため、状況に応じて使用する必要があります。CachedThreadPoolsが短いタスクに適していることに関するJavaドキュメントのコメントは、それらがそのような場合に特に適切であることを示唆しているだけであり、実行時間の長いタスクを伴うタスクに使用できない、または使用すべきではないということではありません。

さらに詳しく説明すると、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は、実際に固定数のスレッドを操作する場合に利点があります。これは、指定したレベルでスレッド数が維持されることを認識しながら、任意の数のタスクをエグゼキューターサービスに送信できるためです。スレッド数を明示的に増やしたい場合は、これは適切な選択ではありません。

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


1
「CachedThreadPoolは、実行時間の長いスレッドに使用してもマイナスの影響がないため、状況に応じて使用する必要があります」。私は同意しないと思います。CachedThreadPoolは、上限のないスレッドを動的に作成します。多数のスレッドで長時間実行されるタスクは、すべてのリソースを占有する可能性があります。また、スレッドの数が理想よりも多いと、これらのスレッドのコンテキスト切り替えに多くのリソースが浪費される可能性があります。回答の最後でカスタムスロットリングが必要であると説明しましたが、回答の最初は少し誤解を招く可能性があります。
Nishit

1
単に境界付きのThreadPoolExecutorように作成しないのはなぜThreadPoolExecutor(0, maximumPoolSize, 60L, TimeUnit.SECONDS, SynchronousQueue())ですか?
Abhijit Sarkar

45

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

CachedThreadPool より危険です FixedThreadPool

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

  1. タスクキューの無制限の性質:メモリ不足または高遅延が発生する可能性があります
  2. 実行時間の長いスレッドはCachedThreadPool、スレッドの作成を制御できなくなります。

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

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

誰かがcommonPoolを使用することを決定した場合はどうなりますか?
Crosk Cool

1
@Ravindra-CachedThreadPoolとFixedThreadPoolの両方の短所を美しく説明しました。これは、並行処理パッケージを深く理解していることを示しています。
アヤカント

5

だから私はたくさんのタスクを実行するスレッド(〜5-150)を生成するプログラムを持っています。

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

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

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


時々、より良い選択がない場合、1つのCPUコアを使用することができますが、すべてのユーザー要求が要求を処理するスレッドをトリガーするサーバーを実行している場合、特に計画を立てている場合、他の合理的な選択はありません。ユーザーベースを拡大したら、サーバーをスケーリングします。
ミシェルファインスタイン2017

@mFeinsteinスレッドプールの実装を選択する立場にある場合、どうしたら選択できないのでしょうか。1 CPUコアの例では、より多くのスレッドを生成するだけではまったく意味がありません。FixedThreadPoolを使用する私の例に完全に適合しています。これは、最初に1つまたは2つのワーカースレッドで、後でコアの数に応じて10または15で、簡単に拡張できます。
ThorstenSchöning2017

2
Webサーバーの実装の大部分は、新しいHTTPリクエストごとに1つの新しいスレッドを作成します...彼らは、マシンが持っている実際のコアの数を気にしません。これにより、実装がより簡単でスケーリングしやすくなります。これは、一度コードを作成してデプロイするだけで、マシンを変更した場合に再コンパイルして再デプロイする必要がない他の多くの設計に当てはまります。
ミシェルファインスタイン

@mFeinsteinほとんどのWebサーバーは、実行できないスレッドの生成が意味をなさない、または接続にイベントループを使用して後でプール内の要求を処理するなどの理由で、要求にスレッドプールを独自に使用します。さらに、あなたは要点を見逃しています。それは、正しいスレッドプールを選択できることと、実行できないスレッドを生成することはまだ意味がないということです。コアに応じてマシンごとに適度なスレッド数に構成されたFixedthreadPoolは問題なくスケーリングされます。
ThorstenSchöning2017

3
@ThorstenSchöning、2コアマシンに50個のCPUにバインドされたスレッドがあることは役に立ちません。2コアマシンに50のIOバインドスレッドがあると、非常に役立ちます。
ポールドレイパー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.