これはのユースケースのように聞こえますExecutorCompletionService
// wrap tasks A, B and C into runnables (or callables if you need some result):
Callable<Result> taskA = ...;
Callable<Result> taskB = ...;
Callable<Result> taskC = ...;
// create an ExecutorCompletionService
// to which you must pass an ExecutorService
// (choose one according to your precise use case)
// (the newCachedThreadPoolExecutor might not be a sensible choice)
ExecutorCompletionService e = new ExecutorCompletionService(Executors.newCachedThreadPoolExecutor());
Set<Future<Result>> futures = new HashSet<>();
// submit your tasks:
futures.add(e.submit(taskA));
futures.add(e.submit(taskB));
futures.add(e.submit(taskC));
// now call take() on the executor completion service,
// which will block the calling thread until the first task has completed
// either succesfully or abruptly (with an exception)
Future<Result> f = e.take();
この後、を呼び出すとf.get()、のインスタンスを取得するかResult、ExectutionException(実行によってスローされた例外をラップする)をスローします。どちらかがすぐに発生します(エグゼキュータ完了サービスのおかげで)。
次に、それに応じて対応します。f.get()例外をスローした場合は、セットから削除fし、futuresセットの他の要素(つまり、送信した他のタスク)を繰り返し処理し.cancel()ます。Callablesはキャンセル可能にコーディングする必要があります。そうしないと、toの呼び出しは.cancel()何もしません。