0Pricing
Java Academy · レッスン

Future の結合:allOf と anyOf

allOf ですべての Future を待ち、anyOf で競合させて、タイムアウトとフォールバックのロジックを実装します。

「Future の結合:allOf と anyOf」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

Future を組み合わせる理由

独立した非同期タスクを並列実行できる場合、次の処理に進む前に、すべてのタスクまたは最初のタスクの完了を待ちたいことがあります。allOf と anyOf は、これら両方のケースに対応します。

allOf: すべての Future を待つ

CompletableFuture.allOf(futures...) は、入力されたすべての future が完了したときに完了する CompletableFuture<Void> を返します。いずれかが失敗すると、allOf も失敗します。

CompletableFuture<String> f1 = fetchUserAsync();
CompletableFuture<List<Order>> f2 = fetchOrdersAsync();
CompletableFuture<Double> f3 = fetchBalanceAsync();
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
all.join(); // wait for all three
String user   = f1.join();
List<Order> orders = f2.join();
Double balance = f3.join();

allOf の結果を収集する

allOf は Void を返すため、allOf.join() の後に個々の future で join() を呼び出して結果を収集します。

List<CompletableFuture<String>> futures = List.of(
    fetchAsync("url1"), fetchAsync("url2"), fetchAsync("url3"));
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
    .thenApply(v -> futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList()))
    .thenAccept(results -> results.forEach(System.out::println))
    .join();

anyOf: 最初に完了したものを採用する

CompletableFuture.anyOf(futures...) は、型に関係なく、最初に完了した future の結果で完了する CompletableFuture<Object> を返します。

CompletableFuture<Object> first = CompletableFuture.anyOf(
    fetchFromCacheAsync(),
    fetchFromDbAsync(),
    fetchFromApiAsync()
);
System.out.println("Fastest result: " + first.join());

タイムアウトのフォールバックに anyOf を使う

実際の計算とタイムアウト用の future を競わせます。先に完了した方を採用することで、非ブロッキングなタイムアウトパターンを実装できます。

CompletableFuture<String> real    = fetchDataAsync();
CompletableFuture<String> timeout = CompletableFuture
    .supplyAsync(() -> {
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        return "timeout-default";
    });
String result = (String) CompletableFuture.anyOf(real, timeout).join();

orTimeout(Java 9 以降)

orTimeout(time, unit) は、時間内に完了しなければ future を TimeoutException で完了させます。手動で anyOf とタイムアウトを組み合わせるパターンよりも簡潔です。

CompletableFuture<String> result = fetchDataAsync()
    .orTimeout(3, TimeUnit.SECONDS)
    .exceptionally(ex -> "timeout-default");
System.out.println(result.join());

allOf による HTTP 呼び出しの並列化

複数のサービスに並列で処理を振り分け、その後で結果を結合します。サービスが独立している場合、逐次呼び出しよりも大幅に高速です。

List<String> urls = List.of(url1, url2, url3);
List<CompletableFuture<String>> futures = urls.stream()
    .map(url -> CompletableFuture.supplyAsync(() -> httpGet(url)))
    .collect(Collectors.toList());
List<String> responses = CompletableFuture
    .allOf(futures.toArray(new CompletableFuture[0]))
    .thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()))
    .join();

allOf のエラー処理

allOf 内のいずれかの future が失敗すると、返される future は直ちに失敗します。個々の future を exceptionally でラップして、1 つの失敗がバッチ全体の失敗につながらないようにします。

List<CompletableFuture<String>> safe = futures.stream()
    .map(f -> f.exceptionally(ex -> "ERROR: " + ex.getMessage()))
    .collect(Collectors.toList());
CompletableFuture.allOf(safe.toArray(new CompletableFuture[0])).join();

異なる型の Future で allOf を使う

allOf は異なる型の future を受け取れます。結合後、それぞれの future の結果を期待する型にキャストします。

CompletableFuture<User> userFuture   = fetchUser(id);
CompletableFuture<Config> configFuture = fetchConfig();
CompletableFuture.allOf(userFuture, configFuture).join();
User   user   = userFuture.join();
Config config = configFuture.join();

allOf と thenApply を組み合わせる

allOf に thenApply を連結すると、すべての future が完了した時点で結果を集約できます。

CompletableFuture<String> summary =
    CompletableFuture.allOf(f1, f2, f3)
        .thenApply(v -> "User: " + f1.join() +
                       ", Orders: " + f2.join().size() +
                       ", Balance: " + f3.join());
System.out.println(summary.join());

スレッドプールに関する考慮事項

allOf / anyOf 呼び出し内のすべての future は同時に実行できます。スレッドプールに十分なスレッドを確保するか、Java 21 以降では仮想スレッドを使用して枯渇を防いでください。

// With virtual threads (Java 21+):
ExecutorService vt = Executors.newVirtualThreadPerTaskExecutor();
CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> httpGet(url), vt);

キャンセルの伝播

allOf / anyOf は、いずれかの future が完了またはキャンセルされても、残りの future を自動的にはキャンセルしません。必要に応じて個別にキャンセルしてください。

CompletableFuture<Object> first = CompletableFuture.anyOf(f1, f2, f3);
first.thenRun(() -> Stream.of(f1, f2, f3).forEach(f -> f.cancel(true)));

確認問題

CompletableFuture.anyOf は何を返しますか。

まとめ

allOf はすべての future を待機します(ファンイン)。anyOf は最初の結果を採用します(競争)。タイムアウトには orTimeout を使用します。個々の future を exceptionally でラップして、1 つの失敗がバッチ全体の失敗につながらないようにします。

よくある質問

「Future の結合:allOf と anyOf」レッスンは無料ですか?

はい。「Future の結合:allOf と anyOf」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「Future の結合:allOf と anyOf」で何を学びますか?

allOf ですべての Future を待ち、anyOf で競合させて、タイムアウトとフォールバックのロジックを実装します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「Future の結合:allOf と anyOf」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. CompletableFuture の作成と完了
  2. thenApply と thenCompose による連鎖
  3. Future の結合:allOf と anyOf
  4. エラー処理と非同期 HTTP パイプライン
← Java Academyに戻る