0Pricing
Java Academy · Lekcja

Łączenie future: allOf i anyOf

Czekaj na wszystkie future za pomocą allOf i wyściguj je przez anyOf, aby implementować logikę limitów czasu i awaryjnych wartości.

Łączenie future: allOf i anyOf to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Dlaczego łączyć future?

Gdy niezależne zadania asynchroniczne mogą być wykonywane równolegle, należy zaczekać na ukończenie wszystkich z nich lub pierwszego z nich przed przejściem dalej. allOf i anyOf obsługują oba przypadki.

allOf: Oczekiwanie na wszystkie future

CompletableFuture.allOf(futures...) zwraca CompletableFuture<Void>, który kończy się po ukończeniu WSZYSTKICH wejściowych future. Jeśli którykolwiek zakończy się niepowodzeniem, allOf również kończy się niepowodzeniem.

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();

Zbieranie wyników allOf

Ponieważ allOf zwraca Void, wyniki należy zebrać, wywołując join() dla każdego pojedynczego future po wykonaniu allOf.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: Wygrywa pierwszy ukończony

CompletableFuture.anyOf(futures...) zwraca CompletableFuture<Object>, który kończy się wynikiem pierwszego ukończonego future, niezależnie od jego typu.

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

anyOf jako rozwiązanie awaryjne dla limitu czasu

Należy uruchomić rzeczywiste obliczenie i future limitu czasu równolegle. Wygrywa ten, który zakończy się pierwszy — w ten sposób można zaimplementować nieblokujący wzorzec limitu czasu.

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) kończy future wyjątkiem TimeoutException, jeśli obliczenie nie zakończy się na czas. Jest to prostsze rozwiązanie niż ręczny wzorzec anyOf z limitem czasu.

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

Równoległe wywołania HTTP za pomocą allOf

Należy równolegle wysłać żądania do wielu usług, a następnie połączyć wyniki. W przypadku niezależnych usług jest to znacznie szybsze niż wywołania sekwencyjne.

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();

Obsługa błędów w allOf

Jeśli jeden z future w allOf zakończy się niepowodzeniem, zwrócony future również kończy się niepowodzeniem. Poszczególne future należy opakować za pomocą exceptionally, aby pojedynczy błąd nie powodował niepowodzenia całej partii.

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();

allOf z różnymi typami

allOf przyjmuje future różnych typów. Po wykonaniu operacji join wynik każdego future należy rzutować na oczekiwany typ.

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

Łączenie allOf i thenApply

Należy połączyć thenApply z allOf, aby agregować wyniki natychmiast po ukończeniu wszystkich 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());

Kwestie dotyczące puli wątków

Wszystkie future w wywołaniu allOf / anyOf mogą być wykonywane równolegle. Należy upewnić się, że pula wątków ma ich wystarczająco dużo, lub użyć wątków wirtualnych (Java 21+), aby uniknąć zagłodzenia.

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

Propagowanie anulowania

allOf / anyOf nie anulują automatycznie pozostałych future, gdy jeden z nich zakończy się lub zostanie anulowany. W razie potrzeby należy anulować każdy z nich osobno.

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

Szybkie sprawdzenie

Co zwraca CompletableFuture.anyOf?

Podsumowanie

allOf czeka na wszystkie future (fan-in). anyOf przyjmuje pierwszy wynik (wyścig). Do obsługi limitów czasu należy używać orTimeout. Poszczególne future należy opakować za pomocą exceptionally, aby pojedynczy błąd nie powodował niepowodzenia całej partii.

Często zadawane pytania

Czy lekcja „Łączenie future: allOf i anyOf” jest bezpłatna?

Tak — pełny tekst „Łączenie future: allOf i anyOf” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Łączenie future: allOf i anyOf”?

Czekaj na wszystkie future za pomocą allOf i wyściguj je przez anyOf, aby implementować logikę limitów czasu i awaryjnych wartości. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Łączenie future: allOf i anyOf”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Tworzenie i uzupełnianie CompletableFuture
  2. Łączenie za pomocą thenApply i thenCompose
  3. Łączenie future: allOf i anyOf
  4. Obsługa błędów i asynchroniczny potok HTTP
← Powrót do Java Academy