web-dev-qa-db-ja.com

ExecutorServiceとカジュアルスレッドスポナー

JavaでのExecutorServiceの動作について基本的な質問があります。

単純にThreadsを作成していくつかのタスクを並行して実行することと、各タスクをThreadPoolに割り当てることの違いを理解するのは非常に困難です。

ExecutorServiceも非常にシンプルで効率的に使用できるように見えるので、なぜそれをいつも使用しないのか疑問に思いました。

それは単に他の方法よりも速くジョブを実行することの問題ですか?

2つの方法の違いを示す2つの非常に単純な例を次に示します。

executorサービスの使用:Hello World(タスク)

static class HelloTask implements Runnable {
    String msg;

    public HelloTask(String msg) {
        this.msg = msg; 
    }
    public void run() {
        long id = Thread.currentThread().getId();
        System.out.println(msg + " from thread:" + id);
    }
}

executorサービスの使用:Hello World(executorの作成、送信)

static class HelloTask {
    public static void main(String[] args) {
        int ntasks = 1000;
        ExecutorService exs = Executors.newFixedThreadPool(4);

        for (int i=0; i<ntasks; i++) { 
            HelloTask t = new HelloTask("Hello from task " + i);    
            exs.submit(t);
        }
        exs.shutdown();
    }
}

以下は、同様の例を示していますが、Callableインターフェースを拡張しています。2つの違いを教えてください。どちらの場合に、一方を他方の代わりに使用すべきですか。

executorサービスの使用:カウンター(タスク)

static class HelloTaskRet implements Callable<Long> {
    String msg;

    public HelloTaskRet(String msg) {
        this.msg = msg; }

        public Long call() {
        long tid = Thread.currentThread().getId(); 
        System.out.println(msg + " from thread:" + tid); 
        return tid;
    } 
}

executorサービスの使用:(作成、送信)

static class HelloTaskRet {
    public static void main(String[] args) {
        int ntasks = 1000;
        ExecutorService exs = Executors.newFixedThreadPool(4);

        Future<Long>[] futures = (Future<Long>[]) new Future[ntasks];

        for (int i=0; i<ntasks; i++) { 
            HelloTaskRet t = new HelloTaskRet("Hello from task " + i);
            futures[i] = exs.submit(t);
        }
        exs.shutdown();
    }
}
22
Phil Moesch

質問とサンプルコードは相関していませんが、両方を明確にしてみます。スレッドを無計画に生成することに対するExecutorServiceの利点は、スレッドが予測どおりに動作し、JVMで比較的大きなスレッド作成のオーバーヘッドを回避できることです(たとえば、スレッドごとにメモリを予約する必要があります)。予測可能性によって、少なくともfixedThreadPoolについては、同時スレッドの最大数がわかっており、それらがいつどのように作成されるかがわかっている(突然のピーク時にJVMが爆発しないようにする) 。

Vince Emigh:ExecutorServiceは、最大値がないcachedThreadPoolもサポートします。人々がExecutorServiceを使用することを選択する主な理由は、(worker threadsを使用して)複数のスレッドを作成するオーバーヘッドを防ぐためです。主に、多くの小さなタスクを別のスレッドで実行する必要がある場合に使用されます。また、singleThreadExecutorについても忘れないでください。

ここで、RunnableCallableのトピックについて、例から簡単に理解できます。 Callablesは値のプレースホルダー(Future)を返すことができ、将来的に実際の値が入力されます。 Runnablesは何も返すことができません。

By Vince Emigh:Runnableも例外をスローできませんが、Callableは例外をスローできます。

22
kaqqao

ExecutorService は、プレーンスレッドと比較して多くの利点を提供します

  1. Threadsのライフサイクルを作成/管理/制御し、スレッド作成コストのオーバーヘッドを最適化できます
  2. タスクの処理を制御できます(Work Stealing、ForkJoinPool、invokeAll)など。
  3. Future時間でタスクをスケジュールできます
  4. スレッドの進行状況と状態を監視できます

単一のスレッドでも、Executors.newFixedThreadPool(1);を使用することを好みます

関連するSEの質問をご覧ください。

JavaのFork/JoinとExecutorService-いつどちらを使用するか?

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

12
Ravindra babu