Классическая задача: отправили в пул десять задач, разного времени выполнения, и нужно обработать результат каждой как можно раньше. Наивный вариант — собрать List> и вызывать get() по очереди в порядке добавления.
Проблема в том, что порядок добавления не совпадает с порядком завершения. Если первая задача в списке — самая долгая, вызов get() на ней блокирует обработку результатов второй и третьей, даже если они давно готовы и просто ждут своей очереди в списке.
CompletionService решает именно это. Он оборачивает Executor и добавляет внутреннюю очередь завершённых задач: как только задача закончилась, её Future попадает в очередь, а take() отдаёт результаты в порядке завершения, а не в порядке отправки.
ExecutorService pool = Executors.newFixedThreadPool(4); CompletionService ecs = new ExecutorCompletionService<>(pool);
for (Task task : tasks) { ecs.submit(task::call); }
for (int i = 0; i < tasks.size(); i++) { Future done = ecs.take(); process(done.get()); }
Следом спросят про таймаут на ожидание конкретной задачи. У CompletionService нет способа узнать, какая именно задача завершилась, если вам это важно — только сам результат. Если нужно сопоставить результат с исходной задачей, оборачивают в обёртку, которая несёт идентификатор задачи вместе с результатом, либо используют CompletableFuture.anyOf с явной привязкой к задаче через thenApply.
CompletionService не ускоряет вычисления — он просто меняет порядок, в котором вы узнаёте о готовых результатах, и это снимает искусственную блокировку на медленной задаче в начале списка.
Тренажёр: 900+ вопросов и задач, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки
· вчера
В очередь или в таблицу они плюнуть не могут?
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
коммент удалён