Вопрос с собеседования · Java

Какое ограничение Future решает CompletableFuture?

MiddleМногопоточность
Ещё спрашивают так:
  • Чем CompletableFuture лучше обычного Future и как в нём обрабатывают ошибки?
Коротко

Future — дескриптор результата с единственным способом получить значение: блокирующий get(). CompletableFuture позволяет описать цепочку продолжений (thenApply, thenCompose, thenCombine, allOf), которая выполнится по готовности, — без блокировки потока.

  • Ошибка едет по цепочке, минуя then-этапы, до exceptionally или handle
  • join и get оборачивают исключение в CompletionException или ExecutionException
  • Можно завершить вручную: complete, completeExceptionally

А ещё Async-стадии без своего executor идут в общий commonPool

Подробно

Ограничения Future: результат забирают блокирующим get(). Чтобы объединить два асинхронных вызова, нужно заблокировать поток на каждом. Колбэков нет, комбинировать нельзя.

CompletableFuture реализует Future и CompletionStage:

CompletableFuture<User> user = supplyAsync(() -> loadUser(id), io);
CompletableFuture<List<Order>> orders = supplyAsync(() -> loadOrders(id), io);
user.thenCombine(orders, Profile::new)
    .thenApply(this::render)
    .exceptionally(e -> fallbackPage())
    .thenAccept(this::send);
  • thenApply — преобразовать значение;
  • thenCompose — следующий шаг сам асинхронный (развернуть вложенный);
  • thenCombine — объединить два независимых результата;
  • allOf, anyOf — ждать группу.

Ошибки. Исключение в стадии не летит сразу: future завершается с ошибкой, следующие then* пропускаются, пока не встретится exceptionally, handle или whenComplete. При join() исключение приходит в CompletionException, при get() — в ExecutionException; настоящая причина — в getCause().

Потоки. Продолжение без суффикса Async может выполниться в потоке, завершившем предыдущую стадию, или в вызывающем. Async-варианты без аргумента используют ForkJoinPool.commonPool() — блокирующие операции отдают в свой executor.

Что хотят услышать

  • Блокирующий get и отсутствие композиции у Future.
  • Основные операции композиции.
  • Как распространяется и обрабатывается ошибка.
  • Где выполняются стадии.

Частые ошибки

  • Вызывать get внутри цепочки, превращая её снова в блокирующую.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Future нельзя отменить, а CompletableFuture можно
  • ✓get() у Future блокирует; CompletableFuture даёт неблокирующие продолжения
  • ✗Future не умеет возвращать результат, а CompletableFuture умеет
  • ✗Future работает только в одном потоке

Что спросят дальше

  • Что делает supplyAsync против runAsync?
  • Как объединить два независимых CompletableFuture?
  • Чем thenApply отличается от thenCompose?
  • Что изменилось с приходом структурной конкурентности?

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования