newCachedThreadPool()
対 newFixedThreadPool()
いつどちらを使用すればよいですか?リソース使用率の面でどの戦略が優れていますか?
これらの2つの関数の違いと使用法については、ドキュメントでかなりよく説明されていると思います。
共有無制限キューで動作するスレッドの固定数を再利用するスレッドプールを作成します。どの時点でも、最大でnThreadsスレッドがアクティブな処理タスクになります。すべてのスレッドがアクティブなときに追加のタスクが送信されると、スレッドが使用可能になるまでキューで待機します。シャットダウン前の実行中に障害が発生してスレッドが終了した場合、後続のタスクを実行する必要がある場合は、新しいスレッドが代わりに使用されます。プール内のスレッドは、明示的にシャットダウンされるまで存在します。
必要に応じて新しいスレッドを作成するスレッドプールを作成しますが、以前に構築されたスレッドが利用可能になると再利用します。これらのプールは通常、短時間の多くの非同期タスクを実行するプログラムのパフォーマンスを向上させます。実行の呼び出しは、利用可能な場合、以前に構築されたスレッドを再利用します。既存のスレッドが利用できない場合、新しいスレッドが作成され、プールに追加されます。 60秒間使用されなかったスレッドは終了し、キャッシュから削除されます。したがって、十分に長い間アイドル状態のままであるプールは、リソースを消費しません。 ThreadPoolExecutorコンストラクタを使用して、プロパティは似ているが詳細が異なるプール(タイムアウトパラメータなど)を作成できることに注意してください。
リソースに関しては、newFixedThreadPool
は、明示的に終了するまですべてのスレッドを実行し続けます。 newCachedThreadPool
では、60秒間使用されていないスレッドは終了し、キャッシュから削除されます。
これを考えると、リソース消費は状況に大きく依存します。たとえば、長時間実行されるタスクが多数ある場合は、FixedThreadPool
をお勧めします。 CachedThreadPool
に関しては、ドキュメントは「これらのプールは通常、短時間の多くの非同期タスクを実行するプログラムのパフォーマンスを向上させる」と述べています。
他の回答を完了するために、Joshua BlochのChapter 10、Item 68によるEffective Java、第2版を引用したいと思います。
「特定のアプリケーションのエグゼキュータサービスを選択するのは難しい場合があります。小さなプログラム、または軽負荷のサーバーを作成している場合、Executors.new- CachedThreadPoolを使用すると一般に良い選択、それは設定を要求せず、一般に「正しいことをする」。しかし、キャッシュされたスレッドプールは良い選択ではないのためにロードされた本番サーバー!
キャッシュスレッドプール、送信されたタスクはキューに入れられませんが、すぐに実行のためにスレッドに渡されます。利用可能なスレッドがない場合、新しいスレッドが作成されます。サーバーの負荷が非常に高く、そのすべてのCPUが完全に使用され、タスクが増えると、スレッドが作成され、事態が悪化するだけです。
したがって、負荷の高い本番サーバーでは、より多くの方がExecutors.newFixedThreadPoolを使用することをお勧めします。最大限に制御します。 "
Grepcodeにコードがある場合は、内部で 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>());
}
そうです、Executors.newCachedThreadPool()
は、複数のクライアントと同時リクエストを処理するサーバーコードには適していません。
どうして?基本的に、2つの(関連する)問題があります。
これは無制限です。つまり、サービスにより多くの作業を投入するだけで、誰でもJVMを不自由にすることができます(DoS攻撃)。スレッドは無視できない量のメモリを消費し、進行中の作業に基づいてメモリ消費も増加するため、この方法でサーバーを簡単に倒すことができます(他のサーキットブレーカーがない場合)。
制限のない問題は、Executorの前にSynchronousQueue
が置かれているという事実によって悪化しています。これは、タスクギバーとスレッドプールの間に直接のハンドオフがあることを意味します。すべての既存のスレッドがビジーの場合、新しいタスクはそれぞれ新しいスレッドを作成します。これは一般に、サーバーコードの不適切な戦略です。 CPUが飽和状態になると、既存のタスクの完了に時間がかかります。さらに多くのタスクが送信され、より多くのスレッドが作成されるため、タスクの完了にはますます時間がかかります。 CPUが飽和状態になると、サーバーが必要とするスレッド数が増えることは間違いありません。
私の推奨事項は次のとおりです。
固定サイズのスレッドプール Executors.newFixedThreadPool または ThreadPoolExecutor。 を設定し、スレッドの最大数を設定します。
Callable/Runnableタスクの無制限のキューについて心配していない場合は、それらのいずれかを使用できます。ブルーノが示唆するように、私はこれら2つの間のnewFixedThreadPool
よりもnewCachedThreadPool
を好む。
ただし、 ThreadPoolExecutor は、newFixedThreadPool
またはnewCachedThreadPool
の両方と比較して、より柔軟な機能を提供します
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
利点:
BlockingQueueサイズを完全に制御できます。以前の2つのオプションとは異なり、無制限ではありません。システム内の予期せぬ乱気流で保留中のCallable/Runnableタスクが大量に蓄積されるため、メモリ不足エラーは発生しません。
カスタム拒否処理を実装できますポリシーORポリシーのいずれかを使用:
デフォルトのThreadPoolExecutor.AbortPolicy
では、ハンドラーは拒否時にランタイムRejectedExecutionExceptionをスローします。
ThreadPoolExecutor.CallerRunsPolicy
では、execute自体を呼び出すスレッドがタスクを実行します。これにより、新しいタスクが送信される速度を遅くする単純なフィードバック制御メカニズムが提供されます。
ThreadPoolExecutor.DiscardPolicy
では、実行できないタスクは単純にドロップされます。
ThreadPoolExecutor.DiscardOldestPolicy
では、executorがシャットダウンされない場合、ワークキューの先頭にあるタスクが削除され、実行が再試行されます(再度失敗する可能性があり、これが繰り返されます)。
以下のユースケースのカスタムスレッドファクトリを実装できます。
NewCachedThreadPoolを使用する必要があるのは、Javadocに記載されているように短期間の非同期タスクがある場合のみです。処理に時間がかかるタスクを送信すると、作成されるスレッドが多くなりすぎます。 newCachedThreadPool( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/ )に高速で長時間実行されるタスクを送信すると、CPU使用率が100%になる可能性があります。
私はいくつかの簡単なテストを行い、次の結果が得られました。
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を使用している場合:
スレッドが最小サイズから最大サイズに増加することはありません。つまり、スレッドプールは最小サイズとして固定サイズになります。