web-dev-qa-db-ja.com

Java.util.concurrent.FutureTaskを使用するのに良い方法ですか?

まず第一に、私はAPI Java.util.concurrentにまったく慣れていないので、多分私がやっていることは完全に間違っていると言わなければなりません。

何をしたいですか?

Java基本的に2つの別個の処理を実行するアプリケーション(myFirstProcessmySecondProcess)がありますが、これらの処理は同じで実行する必要があります時間。

だから、私はそれをしようとしました:

public void startMyApplication() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
    FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
    executor.execute(futureOne);
    executor.execute(futureTwo);
    while (!(futureOne.isDone() && futureTwo.isDone())) {
        try {
            // I wait until both processes are finished.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    logger.info("Processing finished");
    executor.shutdown();
    // Do some processing on results
    ...
}

myFirstProcessおよびmySecondProcessは、Callable<Object>、およびすべての処理はcall()メソッドで行われます。

うまく機能していますが、それが正しい方法であるかどうかはわかりません。私がしたいことをする良い方法ですか?そうでない場合は、コードを強化するためのヒントを教えてください(それでも、できるだけシンプルに保ちます)。

37
Romain Linsolas

get()メソッドを使用したほうがよいでしょう。

_futureOne.get();
futureTwo.get();
_

どちらも、処理が終了したというスレッドからの通知を待機します。これにより、現在使用しているビジー待機タイマーが節約されますが、これは効率的でもエレガントでもありません。

ボーナスとして、API get(long timeout, TimeUnit unit)があります。これにより、スレッドがスリープして応答を待つ最大時間を定義でき、そうでなければ実行を継続できます。

詳細については、 Java API を参照してください。

43
Yuval Adam

上記のFutureTaskの使用は許容できますが、完全に慣用的ではありません。実際には、FutureTaskに送信したものの周りにextraExecutorServiceをラップしています。 FutureTaskは、RunnableによってExecutorServiceとして扱われます。内部的に、FutureTask- as -Runnableを新しいFutureTaskにラップし、_Future<?>_として返します。

代わりに、_Callable<Object>_インスタンスを CompletionService に送信する必要があります。 submit(Callable<V>)を介して2つのCallablesをドロップし、次に向きを変えてCompletionService#take()を2回呼び出します(送信されたCallableごとに1回)。これらの呼び出しは、1つ送信されたタスクが完了するまでブロックされます。

既にExecutorが手元にある場合は、その周りに新しい ExecutorCompletionService を作成し、そこにタスクをドロップします。スピンして眠らないでください。 CompletionService#take()は、タスクのいずれかが完了するか(実行が終了するかキャンセルされるかのいずれか)、またはtake()で待機しているスレッドが中断されるまでブロックします。

18
seh

Yuvalのソリューションは問題ありません。別の方法として、これを行うこともできます:

ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
  executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  // interrupted
}

このアプローチの利点は何ですか?この方法では、エグゼキューターがそれ以上のタスクを受け入れないようにすることを除いて、実際には大きな違いはありません(他の方法でも可能です)。私はこのイディオムをその方よりも好む傾向があります。

また、いずれかのget()が例外をスローした場合、両方のタスクが完了したと想定するコードの一部で終わる可能性があり、これは悪い可能性があります。

17
cletus

Invokeall(Collection ....)メソッドを使用できます

package concurrent.threadPool;

import Java.util.Arrays;
import Java.util.List;
import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;

public class InvokeAll {

    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List<Future<Java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));

        System.out.println(futureList.get(1).get());
        System.out.println(futureList.get(0).get());
    }

    private static class Task1<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 10);
            return (String) "1000 * 5";
        }

    }

    private static class Task2<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 2);
            int i=3;
            if(i==3)
                throw new RuntimeException("Its Wrong");
            return (String) "1000 * 2";
        }

    }
}
7
HakunaMatata

スレッドを同時に開始したい場合、またはスレッドの終了を待ってからさらに処理を行う場合は、 CyclicBarrier を使用できます。詳細については、javadocを参照してください。

5
Louis Jacomet

FutureTasksが2を超える場合は、[ListenableFuture][1]

別の操作が開始されるとすぐに複数の操作が開始される必要がある場合-"fan-out"-ListenableFutureが機能するのは、要求されたコールバックをすべてトリガーするためです。もう少し作業を行うことで、「fan-in」またはListenableFutureをトリガーして、他のいくつかの先物がすべて終了するとすぐに計算を取得できます。

2
Anderson