Что происходит при вызове метода get у Future
Что проверяют
Понимаете ли вы, что асинхронность обычного Future заканчивается ровно на этом методе, и умеете ли разбирать его исключения. Вопрос быстро отделяет тех, кто гонял задачи в пуле, от тех, кто про пул читал.
Как отвечать
Метод блокирует вызывающий поток, пока задача не завершится, и только потом отдаёт результат. Если задача уже готова, возврат происходит сразу. Разбирать надо три исхода:
- задача упала: наружу летит
ExecutionException, а настоящая причина лежит внутри и достаётся черезgetCause(). Логировать надо причину, иначе в логе останется обёртка без смысла; - ждущий поток прервали:
InterruptedException. Флаг прерывания при этом сбрасывается, поэтому его либо восстанавливают на текущем потоке, либо пробрасывают исключение выше; - задачу отменили:
CancellationException, причём непроверяемое, про него забывают в блокахcatch.
Отдельно назовите вариант с таймаутом: он бросает TimeoutException, но саму задачу не останавливает, она продолжает занимать поток пула, пока её не отменить явно. Полезно добавить, что отмена срабатывает, только если задача сама проверяет прерывание, и что для цепочек и объединения результатов берут CompletableFuture, потому что обычный Future ничего, кроме ожидания, не умеет.
Где спотыкаются
Отправляют задачу и тут же ждут её в том же витке цикла. Получается последовательное выполнение плюс накладные расходы на пул: сначала отправляют все задачи, собирают список, и только потом обходят его и забирают результаты.
Ждут без ограничения по времени внутри обработчика запроса. Одна зависшая задача держит поток обработки, за ним копится очередь, и сервис перестаёт отвечать целиком; ожидание внешнего вызова всегда идёт с таймаутом и с понятным поведением после него.
Проглатывают обёртку. Пустой catch или запись в лог самого ExecutionException прячет исходную ошибку, и при разборе инцидента остаётся стек, указывающий на место ожидания, а не на место падения.