187

newCachedThreadPool()newFixedThreadPool()

いつどちらを使うべきですか?リソース使用率の観点から、どちらの戦略が優れていますか?

4

8 に答える 8

225

ドキュメントは、これら2つの関数の違いと使用法をかなりよく説明していると思います。

newFixedThreadPool

共有の無制限キューで動作する固定数のスレッドを再利用するスレッドプールを作成します。どの時点でも、最大でnThreadsスレッドがアクティブな処理タスクになります。すべてのスレッドがアクティブなときに追加のタスクが送信されると、スレッドが使用可能になるまでキューで待機します。シャットダウン前の実行中に障害が発生したためにスレッドが終了した場合、後続のタスクを実行するために必要な場合は、新しいスレッドが代わりに使用されます。プール内のスレッドは、明示的にシャットダウンされるまで存在します。

newCachedThreadPool

必要に応じて新しいスレッドを作成するスレッドプールを作成しますが、以前に作成されたスレッドが利用可能になったときに再利用します。これらのプールは通常、多くの短期間の非同期タスクを実行するプログラムのパフォーマンスを向上させます。実行するための呼び出しは、利用可能な場合、以前に構築されたスレッドを再利用します。使用可能な既存のスレッドがない場合は、新しいスレッドが作成され、プールに追加されます。60秒間使用されなかったスレッドは終了し、キャッシュから削除されます。したがって、十分長い間アイドル状態のままであるプールは、リソースを消費しません。ThreadPoolExecutorコンストラクターを使用して、プロパティは似ているが詳細(タイムアウトパラメーターなど)が異なるプールを作成できることに注意してください。

リソースに関しては、newFixedThreadPoolは明示的に終了するまですべてのスレッドを実行し続けます。newCachedThreadPool60秒間使用されなかったスレッドでは、終了してキャッシュから削除されます。

これを考えると、リソース消費は状況に大きく依存します。たとえば、長時間実行されるタスクが大量にある場合は、をお勧めしFixedThreadPoolます。についてはCachedThreadPool、ドキュメントには「これらのプールは通常、多くの短期間の非同期タスクを実行するプログラムのパフォーマンスを向上させる」と書かれています。

于 2009-06-04T09:25:25.213 に答える
85

他の答えを完成させるために、JoshuaBlochによるEffectiveJava、2nd Edition、第10章、アイテム68を引用したいと思います。

「特定のアプリケーションのエグゼキュータサービスを選択するのは難しい場合があります。小さなプログラム負荷の少ないサーバーを作成している場合は、Executors.new- CachedThreadPoolを使用することを勧めします。これは、構成を必要とせず、通常は「正しいことです。」ただし、キャッシュされたスレッドプールは、負荷の高い本番サーバーには適していません

キャッシュされたスレッドプールでは、送信されたタスクはキューに入れられませんが、実行のためにすぐにスレッドに渡されます。使用可能なスレッドがない場合は、新しいスレッドが作成されます。サーバーの負荷が非常に高く、すべてのCPUが完全に使用され、より多くのタスクが到着すると、より多くのスレッドが作成され、事態はさらに悪化します。

したがって、負荷の高い本番サーバーでは、 Executors.newFixedThreadPoolを使用することをお勧めします。これにより、スレッド数が固定されたプールが提供されるか、ThreadPoolExecutorクラスを直接使用して最大限の制御が可能になります。「」

于 2015-12-15T09:21:44.317 に答える
22

ソースコードを見ると、 ThreadPoolExecutorを呼び出していることがわかります。内部的におよびそれらのプロパティを設定します。要件をより適切に制御するために作成できます。

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>());
}
于 2015-09-06T17:41:00.930 に答える
15

呼び出し可能/実行可能タスクの無制限のキューについて心配していない場合は、そのうちの1つを使用できます。ブルーノが示唆しているように、私もこれら2つよりも好きnewFixedThreadPoolです。newCachedThreadPool

ただし、ThreadPoolExecutor は、newFixedThreadPoolまたはnewCachedThreadPool

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

利点:

  1. BlockingQueueのサイズを完全に制御できます。前の2つのオプションとは異なり、無制限ではありません。システムに予期しない混乱が発生した場合に、保留中のCallable / Runnableタスクが大量に積み重なるため、メモリ不足エラーが発生することはありません。

  2. カスタム拒否処理ポリシーを実装するか、次のいずれかのポリシーを使用できます。

    1. デフォルトThreadPoolExecutor.AbortPolicyでは、ハンドラーは拒否時にランタイムRejectedExecutionExceptionをスローします。

    2. ではThreadPoolExecutor.CallerRunsPolicy、execute自体を呼び出すスレッドがタスクを実行します。これにより、新しいタスクが送信される速度を遅くする単純なフィードバック制御メカニズムが提供されます。

    3. ではThreadPoolExecutor.DiscardPolicy、実行できないタスクは単にドロップされます。

    4. ではThreadPoolExecutor.DiscardOldestPolicy、エグゼキュータがシャットダウンされていない場合、ワークキューの先頭にあるタスクがドロップされ、実行が再試行されます(これにより、再度失敗し、これが繰り返される可能性があります)。

  3. 以下のユースケース用にカスタムスレッドファクトリを実装できます。

    1. よりわかりやすいスレッド名を設定するには
    2. スレッドデーモンのステータスを設定するには
    3. スレッドの優先順位を設定するには
于 2015-11-28T16:51:53.627 に答える
15

このThreadPoolExecutorクラスは、多くのExecutorsファクトリメソッドから返されるエグゼキュータの基本実装です。それでは、の観点から固定スレッドプールとキャッシュスレッドプールにアプローチしましょう。ThreadPoolExecutor

ThreadPoolExecutor

このクラスのメインコンストラクターは次のようになります。

public ThreadPoolExecutor(
                  int corePoolSize,
                  int maximumPoolSize,
                  long keepAliveTime,
                  TimeUnit unit,
                  BlockingQueue<Runnable> workQueue,
                  ThreadFactory threadFactory,
                  RejectedExecutionHandler handler
)

コアプールサイズ

corePoolSizeは、ターゲットスレッドプールの最小サイズを決定します。実行するタスクがない場合でも、実装はそのサイズのプールを維持します。

最大プールサイズ

maximumPoolSize、一度にアクティブにできるスレッドの最大数です。

スレッドプールが大きくなり、corePoolSizeしきい値より大きくなった後、エグゼキュータはアイドル状態のスレッドを終了して、corePoolSize再び到達することができます。trueの場合allowCoreThreadTimeOut、エグゼキュータは、コアプールスレッドがkeepAliveTimeしきい値を超えてアイドル状態だった場合でも、それらを終了できます。

つまり、スレッドがkeepAliveTimeしきい値を超えてアイドル状態のままである場合、スレッドに対する需要がないため、スレッドが終了する可能性があります。

キューイング

新しいタスクが入り、すべてのコアスレッドが占有されている場合はどうなりますか?新しいタスクは、そのBlockingQueue<Runnable>インスタンス内でキューに入れられます。スレッドが解放されると、それらのキューに入れられたタスクの1つを処理できます。

BlockingQueueJavaにはさまざまなインターフェースの実装があるため、次のようなさまざまなキューイングアプローチを実装できます。

  1. 制限付きキュー:新しいタスクは、制限付きタスクキュー内にキューに入れられます。

  2. 無制限のキュー:新しいタスクは、無制限のタスクキュー内にキューに入れられます。したがって、このキューはヒープサイズが許す限り大きくなる可能性があります。

  3. 同期ハンドオフ:を使用しSynchronousQueueて新しいタスクをキューに入れることもできます。その場合、新しいタスクをキューに入れるときに、別のスレッドがそのタスクをすでに待機している必要があります。

仕事の提出

ThreadPoolExecutorが新しいタスクを実行する方法は次のとおりです。

  1. 実行中のスレッドが少ない場合corePoolSizeは、指定されたタスクを最初のジョブとして新しいスレッドを開始しようとします。
  2. BlockingQueue#offerそれ以外の場合は、メソッドを使用して新しいタスクをキューに入れようとします 。キューがいっぱいのoffer場合、メソッドはブロックせず、すぐにを返しますfalse
  3. 新しいタスクのキューに失敗した場合(つまり、をoffer返すfalse場合)、このタスクを最初のジョブとして、スレッドプールに新しいスレッドを追加しようとします。
  4. 新しいスレッドの追加に失敗した場合、エグゼキュータはシャットダウンされるか、飽和状態になります。いずれにせよ、提供されたを使用して新しいタスクは拒否されRejectedExecutionHandlerます。

固定スレッドプールとキャッシュスレッドプールの主な違いは、次の3つの要素に要約されます。

  1. コアプールサイズ
  2. 最大プールサイズ
  3. キューイング
+ ----------- + ----------- + ------------------- + ----- ---------------------------- +
| プールタイプ| コアサイズ| 最大サイズ| キューイング戦略|
+ ----------- + ----------- + ------------------- + ----- ---------------------------- +
| 修正済み| n(固定)| n(固定)| 無制限の`LinkedBlockingQueue`|
+ ----------- + ----------- + ------------------- + ----- ---------------------------- +
| キャッシュ| 0 | Integer.MAX_VALUE | `SynchronousQueue` |
+ ----------- + ----------- + ------------------- + ----- ---------------------------- +


固定スレッドプール


仕組みは次のExcutors.newFixedThreadPool(n)とおりです。

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

ご覧のように:

  • スレッドプールのサイズは固定されています。
  • 需要が高いと成長しません。
  • スレッドがかなり長い間アイドル状態である場合、それは縮小しません。
  • これらすべてのスレッドが実行時間の長いタスクで占有されていて、到着率がまだかなり高いとします。エグゼキュータは無制限のキューを使用しているため、ヒープの大部分を消費する可能性があります。残念ながら、私たちはを経験するかもしれませんOutOfMemoryError

いつどちらを使うべきですか?リソース使用率の観点から、どちらの戦略が優れていますか?

リソース管理の目的で同時タスクの数を制限する場合は、固定サイズのスレッドプールが適しているようです

たとえば、エグゼキュータを使用してWebサーバー要求を処理する場合、固定エグゼキュータは要求バーストをより合理的に処理できます。

さらに優れたリソース管理のために、合理的なと組み合わせThreadPoolExecutorた制限付きのBlockingQueue<T>実装でカスタムを作成することを強くお勧めしますRejectedExecutionHandler


キャッシュされたスレッドプール


仕組みは次のExecutors.newCachedThreadPool()とおりです。

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

ご覧のように:

  • スレッドプールは、ゼロスレッドからに成長する可能性がありますInteger.MAX_VALUE。実際には、スレッドプールには制限がありません。
  • いずれかのスレッドが1分を超えてアイドル状態になると、スレッドが終了する場合があります。したがって、スレッドがアイドル状態のままであると、プールが縮小する可能性があります。
  • 新しいタスクが入ってくる間に割り当てられたすべてのスレッドが占有されている場合、新しいスレッドを作成します。これは、新しいタスクSynchronousQueueを受け入れる相手がいない場合、常に失敗するためです。

いつどちらを使うべきですか?リソース使用率の観点から、どちらの戦略が優れていますか?

予測可能な短期間のタスクがたくさんある場合に使用します。

于 2019-11-08T17:34:40.420 に答える
14

そうExecutors.newCachedThreadPool()です、複数のクライアントと同時リクエストを処理するサーバーコードには適していません。

なんで?これには基本的に2つの(関連する)問題があります。

  1. これは無制限です。つまり、サービスにさらに多くの作業を注入するだけで、誰もがJVMを機能不全に陥れる可能性があります(DoS攻撃)。スレッドは無視できない量のメモリを消費し、進行中の作業に基づいてメモリ消費量も増加するため、この方法でサーバーを倒すのは非常に簡単です(他のサーキットブレーカーが設置されていない場合)。

  2. 無制限の問題は、エグゼキュータの前にSynchronousQueueタスクギバーとスレッドプールの間に直接のハンドオフがあることを意味するという事実によって悪化します。既存のすべてのスレッドがビジーの場合、新しいタスクごとに新しいスレッドが作成されます。これは通常、サーバーコードにとって悪い戦略です。CPUが飽和状態になると、既存のタスクの完了に時間がかかります。さらに多くのタスクが送信され、より多くのスレッドが作成されるため、タスクの完了にはますます時間がかかります。CPUが飽和状態になると、サーバーが必要とするスレッドが増えることは間違いありません。

これが私の推奨事項です:

固定サイズのスレッドプールExecutors.newFixedThreadPoolまたは ThreadPoolExecutorを使用します。スレッドの最大数が設定されています。

于 2015-08-25T07:52:27.773 に答える
5

newCachedThreadPoolを使用する必要があるのは、Javadocに記載されているように短期間の非同期タスクがある場合のみです。処理に時間がかかるタスクを送信すると、作成されるスレッドが多すぎます。長時間実行されるタスクをより高速でnewCachedThreadPool(http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/)に送信すると、CPUが100%に達する可能性があります。

于 2014-05-08T18:43:19.003 に答える
1

私はいくつかの簡単なテストを行い、次の結果があります。

1)SynchronousQueueを使用する場合:

スレッドが最大サイズに達すると、以下のような例外を除いて、新しい作業は拒否されます。

スレッド「メイン」の例外java.util.concurrent.RejectedExecutionException:タスクjava.util.concurrent.FutureTask@3fee733dがjava.util.concurrent.ThreadPoolExecutor@5acf9800から拒否されました[実行中、プールサイズ= 3、アクティブスレッド= 3、キューに入れられたタスク= 0、完了したタスク= 0]

java.util.concurrent.ThreadPoolExecutor $ AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)で

2)LinkedBlockingQueueを使用する場合:

スレッドが最小サイズから最大サイズに増加することはありません。つまり、スレッドプールは最小サイズとして固定サイズになります。

于 2018-03-12T21:18:58.593 に答える