Объединение будущих объектов: 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 — локальная установка не требуется.
Все уроки этого курса
- Создание и завершение CompletableFutures
- Объединение thenApply и thenCompose в цепочки
- Объединение будущих объектов: allOf и anyOf
- Обработка ошибок и асинхронный конвейер HTTP