Предположим, у вас есть набор задач A, B, C, D, E
, и вы хотите выполнить каждую из них асинхронно в Executor
и обработать результаты 1 на 1 по мере их завершения.
При Executor
,Вы должны сделать это так:
List<Future<?>> futures = new ArrayList<Future<?>>();
futures.add(executorService.submit(A));
futures.add(executorService.submit(B));
futures.add(executorService.submit(C));
futures.add(executorService.submit(D));
futures.add(executorService.submit(E));
//This loop must process the tasks in the order they were submitted: A, B, C, D, E
for (Future<?> future:futures) {
? result = future.get();
// Some processing here
}
Проблема этого метода в том, что нет гарантии, что задача A
будет выполнена первой.Таким образом, возможно, что основной поток будет блокировать в ожидании завершения задачи A
, когда он может обрабатывать результат другой задачи (скажем, задача B
).Задержка обработки результатов может быть уменьшена с помощью ExecutorCompletionService
.
List<Future<?>> futures = new ArrayList<Future<?>>();
futures.add(executorCompletionService.submit(A));
futures.add(executorCompletionService.submit(B));
futures.add(executorCompletionService.submit(C));
futures.add(executorCompletionService.submit(D));
futures.add(executorCompletionService.submit(E));
//This for loop will process the tasks in the order they are completed,
//regardless of submission order
for (int i=0; i<futures.size(); i++) {
? result = executorCompletionService.take().get();
// Some processing here
}
Так что, по сути, ExecutorCompletionService
можно использовать, чтобы выжать немного больше эффективности, когда порядок выполнения задач обработки не имеет значения.
Однако следует отметить одну важную вещь.Реализация ExecutorCompletionService содержит очередь результатов.Если take
или poll
не вызваны для опустошения этой очереди, произойдет утечка памяти.Некоторые люди используют Future
, возвращаемый submit
для обработки результатов, и это НЕ правильное использование.