web-dev-qa-db-ja.com

ExecutorServiceを使用する利点は何ですか?

ExecutorServiceRunnableコンストラクターに渡すスレッドを実行するよりもThreadを使用する利点は何ですか?

44
devnull

ExecutorServiceは、生のThreadのような下位レベルの抽象化に関連する複雑さの多くを抽象化します。タスクの成功または突然の終了(RunnableまたはCallableで表される)を安全に開始、終了、送信、実行、およびブロックするメカニズムを提供します。

JCiP 、セクション6.2、馬の口からまっすぐ:

Executorは単純なインターフェースかもしれませんが、多種多様なタスク実行ポリシーをサポートする非同期タスク実行のための柔軟で強力なフレームワークの基礎を形成します。デカップリングの標準的な手段を提供します タスク提出 から タスク実行、タスクをRunnableとして記述します。 Executor実装は、統計収集、アプリケーション管理、監視を追加するためのライフサイクルサポートとフックも提供します。 ... 通常、Executorを使用することが、アプリケーションに生産者/消費者設計を実装する最も簡単な方法です。

並列処理の基盤となるインフラストラクチャの実装に時間を費やすのではなく(多くの場合、誤って多大な努力を払って)、j.u.concurrentフレームワークを使用すると、代わりにタスク、依存関係、潜在的な並列処理の構造化に集中できます。大規模な同時アプリケーションの場合、タスクの境界を特定して活用し、j.u.c、より専門的なソリューションを必要とする可能性がある真の同時実行性の課題のはるかに小さなサブセットに集中することができます。

また、ボイラープレートのルックアンドフィールにもかかわらず、 同時実行ユーティリティを要約したOracle APIページ には、それらを使用するための確かな引数が含まれています。

開発者は標準ライブラリクラスをすでに理解している可能性が高いため、アドホックコンカレントコンポーネントのAPIと動作を学ぶ必要はありません。さらに、コンカレントアプリケーションは、信頼性が高く、十分にテストされたコンポーネントで構築されている場合、デバッグがはるかに簡単です。

この SOに関する質問 は、すぐにJCiPである良い本について尋ねます。まだ入手していない場合は、コピーを入手してください。そこに示されている並行性に対する包括的なアプローチは、この問題をはるかに超えており、長期的には多くの心痛を軽減します。

42
andersoj

私が見る利点は、いくつかのスレッドを管理/スケジュールすることです。 ExecutorServiceを使用すると、バグに悩まされる可能性のある独自のスレッドマネージャーを記述する必要がありません。これは、プログラムが一度に複数のスレッドを実行する必要がある場合に特に便利です。たとえば、一度に2つのスレッドを実行する場合、次のように簡単に実行できます。

ExecutorService exec = Executors.newFixedThreadPool(2);

exec.execute(new Runnable() {
  public void run() {
    System.out.println("Hello world");
  }
});

exec.shutdown();

この例は些細なことかもしれませんが、「hello world」行は重い操作で構成されており、プログラムのパフォーマンスを改善するために、その操作を一度に複数のスレッドで実行したいと考えてください。これはほんの一例です。複数のスレッドをスケジュールまたは実行し、スレッドマネージャーとしてExecutorServiceを使用したい場合がまだ多くあります。

単一のスレッドを実行する場合、ExecutorServiceを使用する明確な利点はありません。

18
Manny

従来のスレッドからの次の制限は、Executorフレームワーク(組み込みのスレッドプールフレームワーク)によって克服されます。

  • 貧弱なリソース管理つまり、リクエストごとに新しいリソースを作成し続けます。リソースの作成に制限はありません。 Executorフレームワークを使用すると、既存のリソースを再利用して、リソースの作成に制限をかけることができます。
  • Not Robust:新しいスレッドを作成し続けると、StackOverflowException例外が発生するため、JVMがクラッシュします。
  • 時間のオーバーヘッド作成:リクエストごとに新しいリソースを作成する必要があります。新しいリソースを作成するには時間がかかります。すなわち、スレッド作成>タスク。 Executorフレームワークを使用して、スレッドプールに組み込むことができます。

スレッドプールの利点

  • スレッドプールを使用すると、要求またはタスクの処理中にスレッドが作成されないため、応答時間が短縮されます。

  • スレッドプールを使用すると、必要に応じて実行ポリシーを変更できます。 ExecutorServiceの実装を置き換えるだけで、単一のスレッドから複数のスレッドに移行できます。

  • Javaアプリケーションのスレッドプールは、システムの負荷と利用可能なリソースに基づいて決定された構成された数のスレッドを作成することにより、システムの安定性を高めます。

  • スレッドプールは、アプリケーション開発者をスレッド管理から解放し、ビジネスロジックに集中できるようにします。

ソース

10
Premraj

以下にいくつかの利点を示します。

  1. エグゼキューターサービスは非同期でスレッドを管理します
  2. Callableを使用して、スレッドの完了後に戻り結果を取得します。
  3. 新しい作業を自動的に割り当てるために、作業スレッドを解放し、完了した作業をスレッドから再販するための作業の割り当てを管理する
  4. fork-並列処理の結合フレームワーク
  5. スレッド間のより良いコミュニケーション
  6. invokeAllおよびinvokeAnyは、任意のスレッドまたはすべてのスレッドを一度に実行するためのより多くの制御を提供します
  7. シャットダウンは、すべてのスレッドに割り当てられた作業を完了する機能を提供します
  8. スケジュールされたエグゼキューターサービスは、実行可能ファイルと呼び出し可能オブジェクトの繰り返し呼び出しを生成するメソッドを提供します。
7
Nitin

新しいスレッドを作成するのは本当にそんなに高価ですか?

ベンチマークとして、Runnablesと空のrun()メソッドで60,000個のスレッドを作成しました。各スレッドを作成した後、すぐにそのstart(..)メソッドを呼び出しました。これには、約30秒の激しいCPUアクティビティが必要でした。 この質問 に対応して、同様の実験が行われました。これらの要約は、スレッドがすぐに終了せず、アクティブなスレッドが多数(数千)蓄積すると、問題が発生することです:(1)各スレッドにスタックがあるため、メモリが不足します、(2)OSによって課されるプロセスごとのスレッド数に制限があるかもしれませんが、 必ずしもそうではないようです

ですから、私が見る限り、1秒あたり10個のスレッドを起動することについて話している場合、それらはすべて新しいスレッドが起動するよりも速く終了し、このレートを超えないことを保証できます。目に見える性能や安定性に具体的な利点はありません。 (特定の並行処理のアイデアをコードで表現する方が便利で読みやすいかもしれませんが。)一方、実行に時間がかかる1秒あたり数百または数千のタスクをスケジュールする場合、大きな問題にぶつかります。すぐに。これは予期せずに発生する場合があります。サーバーへのリクエストに応じてスレッドを作成し、サーバーが受信するリクエストの強度が急上昇した場合。しかし、例えばすべてのユーザー入力イベント(キーを押す、マウスの動き)に応答する1つのスレッドは、タスクが短い限り、まったく問題ないようです。

2
Evgeni Sergeev

ExecutorServiceは、FutureTaskへのアクセスも提供します。FutureTaskは、完了したバックグラウンドタスクの結果を呼び出しクラスに返します。 Callableを実装する場合

public class TaskOne implements Callable<String> {

@Override
public String call() throws Exception {
    String message = "Task One here. . .";
    return message;
    }
}

public class TaskTwo implements Callable<String> {

@Override
public String call() throws Exception {
    String message = "Task Two here . . . ";
    return message;
    }
}

// from the calling class

ExecutorService service = Executors.newFixedThreadPool(2);
    // set of Callable types
    Set<Callable<String>>callables = new HashSet<Callable<String>>();
    // add tasks to Set
    callables.add(new TaskOne());
    callables.add(new TaskTwo());
    // list of Future<String> types stores the result of invokeAll()
    List<Future<String>>futures = service.invokeAll(callables);
    // iterate through the list and print results from get();
    for(Future<String>future : futures) {
        System.out.println(future.get());
    }
1
Bade

最大しきい値の制限なしに多数のスレッドを作成すると、アプリケーションのヒープメモリが不足する可能性があります。そのため、ThreadPoolを作成する方がはるかに優れたソリューションです。 ThreadPoolを使用すると、プールして再利用できるスレッドの数を制限できます。

エグゼキュータフレームワークは、Javaでスレッドプールを作成するプロセスを容易にします。 Executorsクラスは、ThreadPoolExecutorを使用してExecutorServiceの簡単な実装を提供します。

出典:

Executors Frameworkとは

0
Virtual

Java 1.5バージョンでは、Thread/Runnableは2つの別個のサービス用に設計されていました

  1. 作業単位
  2. その作業単位の実行

ExecutorServiceは、Runnable/Callableを作業単位として指定し、Executorを(ライフサイクルを使用して)作業単位を実行するメカニズムとして指定することにより、これら2つのサービスを分離します。

0
Anil Saha