私はちょうどこの投稿を読み終えました: Java-7のForkJoinPoolに対するJava-5 ThreadPoolExecutorの利点は何ですか? と答えは十分にストレートではないと感じました。
Java 7のFork-Joinフレームワークと古いソリューションとの間のトレードオフは何ですか?)
また、トピックに関するGoogleのヒット#1を読みました Javaヒント:ForkJoinPool vs ExecutorServiceを使用する場合 from javaworld.com ですが、記事はタイトルの質問に答えませんwhen、それは主にAPIの違いについて話します...
Fork-joinを使用すると、ExecutorService
で実行する場合は手動で実装する必要がある分割および征服ジョブを簡単に実行できます。実際には、ExecutorService
は通常、多くの独立した要求(トランザクション)を同時に処理し、1つの一貫したジョブを加速したい場合にfork-joinするために使用されます。
Fork-joinは、タスクがサブタスクの実行と結果の処理を伴うrecursive問題に特に適しています。 (これは通常、「分割して征服する」と呼ばれます...しかし、それは本質的な特徴を明らかにしません。)
従来のスレッドを使用して(たとえばExecutorServiceを使用して)このような再帰的な問題を解決しようとすると、他のスレッドが結果を配信するのを待つスレッドになります。
一方、問題にこれらの特性がない場合、fork-joinを使用しても実際の利点はありません。
Java 8はエグゼキューターにもう1つのAPIを提供します
_static ExecutorService newWorkStealingPool()
_
使用可能なすべてのプロセッサーをターゲット並列処理レベルとして使用して、ワークスチールスレッドプールを作成します。
このAPIの追加により、 Executors はさまざまなタイプの ExecutorService オプションを提供します。
要件に応じて、いずれかを選択するか、 ThreadPoolExecutor を探して、バウンドタスクキューサイズ、RejectedExecutionHandler
メカニズムをより適切に制御できます。
static ExecutorService newFixedThreadPool(int nThreads)
共有無制限キューで動作するスレッドの固定数を再利用するスレッドプールを作成します。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
所定の遅延後に実行するコマンド、または定期的に実行するコマンドをスケジュールできるスレッドプールを作成します。
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
必要に応じて新しいスレッドを作成するスレッドプールを作成しますが、使用可能な場合は以前に構築されたスレッドを再利用し、必要に応じて提供されたThreadFactoryを使用して新しいスレッドを作成します。
static ExecutorService newWorkStealingPool(int parallelism)
指定された並列処理レベルをサポートするのに十分なスレッドを維持するスレッドプールを作成し、複数のキューを使用して競合を減らすことができます。
これらの各APIは、アプリケーションのそれぞれのビジネスニーズを満たすことを目的としています。どちらを使用するかは、ユースケースの要件によって異なります。
例えば.
送信されたすべてのタスクを到着順に処理する場合は、newFixedThreadPool(1)
を使用します
再帰タスクの大きな計算のパフォーマンスを最適化する場合は、ForkJoinPool
またはnewWorkStealingPool
を使用します
定期的または将来の特定の時間にいくつかのタスクを実行する場合は、newScheduledThreadPool
を使用します
PeterLawrey
のユースケースでExecutorService
によるもう1つのニース 記事 をご覧ください。
関連するSEの質問:
Brian Goetzは状況を最もよく説明しています。 https://www.ibm.com/developerworks/library/j-jtp11137/index.html
従来のスレッドプールを使用してfork-joinを実装することも困難です。fork-joinタスクは、他のタスクを待つために多くの人生を費やしているためです。この動作は、作成されたタスクの数を制限するためにパラメーターが慎重に選択されない限り、またはプール自体が制限されない限り、スレッド枯渇デッドロックのレシピです。従来のスレッドプールは、互いに独立したタスク用に設計されており、潜在的にブロックする粗いタスクを念頭に置いて設計されています。fork-joinソリューションはどちらも生成しません。
フォーク結合プールを使用する理由の良い例があるので、投稿全体を読むことをお勧めします。 ForkJoinPoolが公式になる前に書かれたため、彼が参照するcoInvoke()
メソッドはinvokeAll()
になりました。
Fork-Joinフレームワークは、再帰的マルチスレッドプログラムの「待機」問題に特に対処するためのExecutorフレームワークの拡張機能です。実際、新しいFork-Joinフレームワーククラスはすべて、Executorフレームワークの既存のクラスから拡張されています。
Fork-Joinフレームワークの中心となる2つの特性があります
並列処理のニーズが厳密に再帰的である場合、Fork-Joinを使用する以外に選択肢はありません。そうでない場合は、executorまたはFork-Joinフレームワークのいずれかを実行する必要があります。忙しいスレッドからいくつかのタスクを「盗む」。
Fork JoinはExecuterServiceの実装です。主な違いは、この実装ではDEQUEワーカープールが作成されることです。タスクが片側から挿入され、どこからでも引き出される場所。 new ForkJoinPool()
を作成した場合、使用可能なCPUを探し、その数のワーカースレッドを作成します。次に、各スレッドに均等に負荷を分散します。ただし、1つのスレッドの動作が遅く、他のスレッドの動作が速い場合、遅いスレッドからタスクを選択します。裏側から。以下の手順は、盗みをよりよく説明します。
ステージ1(初期):
W1-> 5,4,3,2,1
W2-> 10,9,8,7,6
ステージ2:
W1-> 5,4
W2-> 10,9,8,7、
ステージ3:
W1-> 10,5,4
W2-> 9,8,7、
一方、Executorサービスは要求された数のスレッドを作成し、ブロッキングキューを適用して、残りの待機タスクをすべて保存します。 cachedExecuterServiceを使用している場合、ジョブごとに単一のスレッドが作成され、待機キューはありません。