Ответ
CompletableFuture<T> — это класс, появившийся в Java 8, который представляет собой отложенный результат асинхронной операции. Он сочетает в себе возможности Future (представление будущего результата) и CompletionStage (возможность строить цепочки и комбинировать асинхронные задачи).
Ключевые возможности:
- Явное завершение: Вы можете завершить
CompletableFutureвручную с результатом (complete(T value)) или исключением (completeExceptionally(Throwable ex)). - Цепочки вызовов (chaining): Позволяет описывать поток обработки: "когда завершится эта задача, сделай то, затем другое".
- Комбинация нескольких Future: Объединение результатов двух и более независимых асинхронных задач.
- Гибкая обработка исключений прямо в цепочке.
- Асинхронное выполнение с использованием пулов потоков (
ForkJoinPool.commonPool()по умолчанию или своего).
Основные методы для построения цепочек:
thenApply(Function)— преобразует результат (синхронно).thenApplyAsync(Function)— преобразует результат асинхронно.thenAccept(Consumer)— потребляет результат без возврата значения.thenCompose(Function)— "плоское" преобразование (композиция Future).thenCombine(CompletionStage, BiFunction)— объединяет результаты двух Future.exceptionally(Function)— обрабатывает исключение, возвращая резервное значение.handle(BiFunction)— обрабатывает и результат, и исключение.
Практический пример: параллельный вызов двух сервисов и объединение результата.
import java.util.concurrent.CompletableFuture;
public class OrderService {
// Имитация асинхронных вызовов
CompletableFuture<Double> getPriceAsync(String productId) {
return CompletableFuture.supplyAsync(() -> {
// Долгий вызов к Price Service
return 99.99;
});
}
CompletableFuture<Double> getDiscountAsync(String userId) {
return CompletableFuture.supplyAsync(() -> {
// Долгий вызов к Discount Service
return 0.1; // 10% скидка
});
}
public CompletableFuture<Double> calculateTotalAsync(String productId, String userId) {
CompletableFuture<Double> priceFuture = getPriceAsync(productId);
CompletableFuture<Double> discountFuture = getDiscountAsync(userId);
// Комбинируем результаты, когда оба Future завершатся
return priceFuture.thenCombine(discountFuture, (price, discountRate) -> {
return price * (1 - discountRate); // Итоговая цена со скидкой
}).exceptionally(ex -> { // Обработка ошибок в любой из задач
System.err.println("Ошибка расчета: " + ex.getMessage());
return 0.0; // Возвращаем значение по умолчанию
});
}
public static void main(String[] args) throws Exception {
OrderService service = new OrderService();
CompletableFuture<Double> totalFuture = service.calculateTotalAsync("prod1", "user42");
// Блокирующее ожидание результата (в реальном коде лучше использовать thenAccept)
Double total = totalFuture.get();
System.out.println("Итоговая цена: " + total); // Выведет: Итоговая цена: 89.991
}
}
Использование: CompletableFuture — основной инструмент для неблокирующего асинхронного программирования в современных Java-приложениях, особенно в микросервисных архитектурах и высоконагруженных веб-сервисах.
Ответ 18+ 🔞
А, блядь, CompletableFuture! Ну это ж, сука, просто песня, а не класс! Появился в Java 8, как гром среди ясного неба, и сразу всем показал, где раки зимуют.
Представь себе, ты раньше с этим Future обычным мучился — как об стенку горох, нихуя не понятно, когда там твой результат подъедет. А тут — овердохуища возможностей! Это как если бы тебе дали не просто обещание, что пиццу привезут, а ещё и возможность сказать: «Когда привезут, разогрей её, потом посыпь перчиком, а потом позови меня, когда будет готова». И всё это — не стоя над курьером с палкой!
Что он умеет, этот хитрожопый ублюдок:
- Сам решать, когда всё готово. Можешь вручную ткнуть ему в морду результат (
complete()) или крикнуть: «Всё, пиздец, ошибка!» (completeExceptionally()). Сам хозяин положения, блядь. - Строить цепочки, как паровозик. «Сделай это — потом то — а потом вот эдак». Это называется
CompletionStage, и это просто волшебство, ёпта. - Скрещивать несколько Future, как бог черепаху с уткой. Ждёт, пока два независимых долбоёба-сервиса ответят, а потом из их ответов лепит одну общую сосиску.
- Ловить исключения прямо в цепочке, не разваливая всё к хуям собачьим. Красота!
- Гонять задачи в пулах потоков асинхронно, чтобы твой главный поток не стоял столбом, как идиот.
Основные рычаги и кнопки, которыми дёргаешь:
thenApply()— взял результат, обработал и отдал дальше. Синхронно, не отходя от кассы.thenApplyAsync()— то же самое, но пинком под жопу отправляет в другой поток, чтобы не засирать текущий.thenAccept()— взял результат, что-то с ним сделал (например, вывел на экран) и пошёл дальше. Ничего не возвращает, как благородный донор.thenCompose()— это, блядь, высший пилотаж. Берешь одинCompletableFuture, а возвращаешь другой. Плоское преобразование, ёбта! Чтобы не былоCompletableFuture<CompletableFuture<T>>— этой хуйни, от которой мозг вскипает.thenCombine()— ждёт два будущих результата, а потом скрещивает их в одном методе. Любовь, блядь, на атомном уровне.exceptionally()— спасательный круг. Если где-то в цепочке всё пошло по пизде, ты здесь можешь подстелить соломки и вернуть какое-нибудь дефолтное значение.handle()— универсальный солдат. Обрабатывает и удачный результат, и пиздец (исключение). Всё в одном флаконе.
Смотри, как это выглядит в жизни, на примере, от которого волосы дыбом встают:
Допустим, тебе надо посчитать цену заказа. Но цена лежит в одном сервисе (который тормозит, как черепаха в сиропе), а скидка — в другом (который отвечает, когда ему вздумается). Раньше бы ты их последовательно вызывал и ждал, как лох. А теперь — хуяк-хуяк, и параллельно!
import java.util.concurrent.CompletableFuture;
public class OrderService {
// Прикинемся, что это долгий вызов к сервису цен
CompletableFuture<Double> getPriceAsync(String productId) {
return CompletableFuture.supplyAsync(() -> {
// Симуляция долгой работы, блядь
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return 99.99; // Цена
});
}
// А это — вызов к сервису скидок, который тоже не шибко шустрый
CompletableFuture<Double> getDiscountAsync(String userId) {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(800); } catch (InterruptedException e) {}
return 0.1; // 10% скидки, мать его
});
}
public CompletableFuture<Double> calculateTotalAsync(String productId, String userId) {
CompletableFuture<Double> priceFuture = getPriceAsync(productId);
CompletableFuture<Double> discountFuture = getDiscountAsync(userId);
// А вот и магия! Ждём ОБА результата, а потом хуячим формулу
return priceFuture.thenCombine(discountFuture, (price, discountRate) -> {
return price * (1 - discountRate); // Итоговая цена
}).exceptionally(ex -> { // Если где-то случилась жопа
System.err.println("Ошибка расчета, ёпта: " + ex.getMessage());
return 0.0; // Отдаём ноль, чтоб не палить систему
});
}
public static void main(String[] args) throws Exception {
OrderService service = new OrderService();
CompletableFuture<Double> totalFuture = service.calculateTotalAsync("prod1", "user42");
// Тут мы ждём результат (в реальности так лучше не делать, а подписываться через thenAccept)
Double total = totalFuture.get(); // get() — это блокирующий вызов, старый дед
System.out.println("Итоговая цена: " + total); // Выведет что-то вроде: Итоговая цена: 89.991
}
}
И где этот красавец применяется? Да везде, сука! Особенно в этих ваших модных микросервисах, где один запрос должен пообщаться с десятком других сервисов. Вместо того чтобы тупо ждать ответа от каждого по очереди (и тратить время, как последний лох), ты запускаешь все запросы параллельно, а потом, как главный дирижёр, собираешь из них общий ответ. Производительность взлетает до небес, а код выглядит почти что поэзией. Ну, или как минимум — грамотно написанной прозой, а не потоком сознания дегенерата.