Future и его ограничения
submit(Callable) возвращает Future — дескриптор будущего результата с get() (блокирующее ожидание), isDone() и cancel(). Это базовый способ получить результат асинхронной задачи.
Ограничения Future жёсткие: get() блокирует поток, нельзя задать колбэк на завершение и нельзя композировать несколько Future в конвейер без блокировок. Именно это решает CompletableFuture.
CompletableFuture: асинхронный результат
CompletableFuture — это Future, на который можно навешивать продолжения, не блокируя поток. Задачу запускают через supplyAsync (с результатом) или runAsync (без), а дальше описывают конвейер преобразований, который выполнится по готовности.
Это сдвиг от «дождитесь и заберите» к «опишите, что сделать с результатом, когда он появится».
CompletableFuture
.supplyAsync(() -> loadUser(id), pool)
.thenApply(User::name)
.thenAccept(System.out::println);
Композиция
thenApply трансформирует результат (как map). thenCompose сцепляет зависимую асинхронную операцию, возвращающую свой CompletableFuture (как flatMap) — без него получился бы вложенный CompletableFuture<CompletableFuture<…>>. thenCombine объединяет два независимых результата.
Для групп: allOf ждёт завершения всех, anyOf — первого. Так строят и параллельные веера, и последовательные цепочки.
async-варианты и пул
У каждого шага есть *Async-вариант (thenApplyAsync), который выполняет продолжение в пуле, а не в потоке, завершившем предыдущий шаг. Без явного Executor используется общий ForkJoinPool.commonPool.
Важное правило: не выполнять блокирующие операции в common pool — он маленький (по числу ядер) и общий на весь процесс; для блокирующих шагов передавайте отдельный пул.
Обработка ошибок
Исключение внутри стадии заворачивается в CompletionException и пробрасывается по цепочке. Его обрабатывают: exceptionally (подставить запасное значение), handle (получить и результат, и исключение), whenComplete (побочный эффект без изменения результата).
Забытая обработка ошибки в CompletableFuture — частый баг: исключение «теряется», если результат никто не забрал.
Как отвечать на собеседовании
Назовите ограничения Future (блокирующий get, нет композиции) как мотивацию CompletableFuture, различите thenApply/thenCompose/thenCombine (map/flatMap/zip) и allOf/anyOf, подчеркните про *Async с явным пулом и запрет блокировок в commonPool, и перечислите exceptionally/handle/whenComplete для ошибок.