0Pricing
Java Academy · Урок

Объединение будущих объектов: allOf и anyOf

Ожидайте завершения всех будущих объектов с помощью allOf и устраивайте гонку между ними с помощью anyOf, реализуя ограничение времени и резервную логику.

«Объединение будущих объектов: allOf и anyOf» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Зачем объединять future

Когда независимые асинхронные задачи могут выполняться параллельно, необходимо дождаться завершения всех задач или первой из них, прежде чем продолжать работу. allOf и anyOf обрабатывают оба случая.

allOf: ожидание всех future

CompletableFuture.allOf(futures...) возвращает CompletableFuture<Void>, который завершается, когда завершаются ВСЕ переданные future. Если какой-либо из них завершается с ошибкой, 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, соберите результаты, вызвав join() для каждого отдельного future после 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: побеждает первый завершившийся

CompletableFuture.anyOf(futures...) возвращает CompletableFuture<Object>, который завершается результатом первого завершившегося future независимо от его типа.

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

Параллельные HTTP-вызовы с allOf

Параллельно отправьте запросы к нескольким сервисам, затем объедините результаты. При независимых сервисах это намного быстрее последовательных вызовов.

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

Если один future в allOf завершается с ошибкой, возвращённый future немедленно завершается с ошибкой. Оберните отдельные future в exceptionally, чтобы один сбой не приводил к сбою всей группы.

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 с разными типами

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

Добавьте thenApply после allOf, чтобы агрегировать результаты сразу после завершения всех 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());

Особенности пулов потоков

Все future в вызове allOf или anyOf могут выполняться одновременно. Убедитесь, что в пуле достаточно потоков, или используйте виртуальные потоки (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, чтобы один сбой не приводил к сбою всей группы.

Часто задаваемые вопросы

Урок «Объединение будущих объектов: allOf и anyOf» бесплатный?

Да — полный текст урока «Объединение будущих объектов: allOf и anyOf» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Объединение будущих объектов: allOf и anyOf»?

Ожидайте завершения всех будущих объектов с помощью allOf и устраивайте гонку между ними с помощью anyOf, реализуя ограничение времени и резервную логику. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Объединение будущих объектов: allOf и anyOf»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Создание и завершение CompletableFutures
  2. Объединение thenApply и thenCompose в цепочки
  3. Объединение будущих объектов: allOf и anyOf
  4. Обработка ошибок и асинхронный конвейер HTTP
← Назад к Java Academy