Как проверить, завершены ли все задачи, запущенные в ExecutorService

У меня есть ConcurrentLinkedDeque, который я использую для синхронных push/pop элементов, и у меня есть некоторые асинхронные задачи, которые берут один элемент из стека, и если этот элемент имеет соседей, он нажимает его на стек.

Пример кода:

private ConcurrentLinkedDeque<Item> stack = new ConcurrentLinkedDeque<>();
private ExecutorService exec = Executors.newFixedThreadPool(5);

    while ((item = stack.pollFirst()) != null) {
                if (item == null) {
                } else {
                    Runnable worker = new Solider(this, item);
                    exec.execute(worker);
                }
            }

   class Solider{
         public void run(){
             if(item.hasNeighbors){
                for(Item item:item.neighbors){
                    stack.push(item)
                }
             } 
         }
    }

Я хотел бы иметь дополнительную инструкцию в цикле while, которая отвечает на вопрос - "любая работа в Executor работает?"

Ответ 1

Нет чистого способа проверить, все ли Runnables выполнены, если вы используете ExecutorService.execute(Runnable). Если вы не создадите механизм для этого в самом Runnable (что, на мой взгляд, небрежно).

Вместо этого:
Используйте ExecutorService.submit(Runnable). Этот метод возвращает Future<?>, который является дескриптором результата Runnable. Использование Futures обеспечивает простой способ проверки результатов.

Все, что вам нужно сделать, это сохранить список отправленных вами фьючерсов, а затем вы можете выполнить итерацию по всему списку фьючерсов, а также:
А) дождитесь, пока все фьючерсы будут сделаны блокирующим способом или
Б) проверьте, все ли фьючерсы сделаны неблокирующим способом.

Вот пример кода:

List<Future<?>> futures = new ArrayList<Future<?>>();
ExecutorService exec = Executors.newFixedThreadPool(5);

// Instead of using exec.execute() use exec.submit()
// because it returns a monitorable future
while((item = stack.pollFirst()) != null){
    Runnable worker = new Solider(this, item);
    Future<?> f = exec.submit(worker);
    futures.add(f);
}

// A) Await all runnables to be done (blocking)
for(Future<?> future : futures)
    future.get(); // get will block until the future is done

// B) Check if all runnables are done (non-blocking)
boolean allDone = true;
for(Future<?> future : futures){
    allDone &= future.isDone(); // check if future is done
}