まず第一に、私はAPI Java.util.concurrentにまったく慣れていないので、多分私がやっていることは完全に間違っていると言わなければなりません。
何をしたいですか?
Java基本的に2つの別個の処理を実行するアプリケーション(myFirstProcess、mySecondProcess)がありますが、これらの処理は同じで実行する必要があります時間。
だから、私はそれをしようとしました:
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()メソッドで行われます。
うまく機能していますが、それが正しい方法であるかどうかはわかりません。私がしたいことをする良い方法ですか?そうでない場合は、コードを強化するためのヒントを教えてください(それでも、できるだけシンプルに保ちます)。
get()
メソッドを使用したほうがよいでしょう。
_futureOne.get();
futureTwo.get();
_
どちらも、処理が終了したというスレッドからの通知を待機します。これにより、現在使用しているビジー待機タイマーが節約されますが、これは効率的でもエレガントでもありません。
ボーナスとして、API get(long timeout, TimeUnit unit)
があります。これにより、スレッドがスリープして応答を待つ最大時間を定義でき、そうでなければ実行を継続できます。
詳細については、 Java API を参照してください。
上記のFutureTask
の使用は許容できますが、完全に慣用的ではありません。実際には、FutureTask
に送信したものの周りにextraExecutorService
をラップしています。 FutureTask
は、Runnable
によってExecutorService
として扱われます。内部的に、FutureTask
- as -Runnable
を新しいFutureTask
にラップし、_Future<?>
_として返します。
代わりに、_Callable<Object>
_インスタンスを CompletionService
に送信する必要があります。 submit(Callable<V>)
を介して2つのCallable
sをドロップし、次に向きを変えてCompletionService#take()
を2回呼び出します(送信されたCallable
ごとに1回)。これらの呼び出しは、1つ送信されたタスクが完了するまでブロックされます。
既にExecutor
が手元にある場合は、その周りに新しい ExecutorCompletionService
を作成し、そこにタスクをドロップします。スピンして眠らないでください。 CompletionService#take()
は、タスクのいずれかが完了するか(実行が終了するかキャンセルされるかのいずれか)、またはtake()
で待機しているスレッドが中断されるまでブロックします。
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()が例外をスローした場合、両方のタスクが完了したと想定するコードの一部で終わる可能性があり、これは悪い可能性があります。
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";
}
}
}
スレッドを同時に開始したい場合、またはスレッドの終了を待ってからさらに処理を行う場合は、 CyclicBarrier を使用できます。詳細については、javadocを参照してください。
FutureTasksが2を超える場合は、[ListenableFuture][1]
。
別の操作が開始されるとすぐに複数の操作が開始される必要がある場合-"fan-out"-ListenableFutureが機能するのは、要求されたコールバックをすべてトリガーするためです。もう少し作業を行うことで、「fan-in」またはListenableFutureをトリガーして、他のいくつかの先物がすべて終了するとすぐに計算を取得できます。