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

Я знаю это shutdown() а также awaitTermination() существовать. Проблема заключается в том, что runnables в пуле должны иметь возможность добавить к нему неизвестный номер (не может использовать обратный отсчет) других runnables, и если я позвоню shutdown() эти задачи будут отклонены. Как я могу знать, когда они закончили?

3 ответа

Решение

Вместо отправки Runnable задачи к Executor Лучше использовать ForkJoinTask / ForkJoinPool вместо. ForkJoinTask работает внутри ForkJoinPool и может порождать произвольное количество (под) задач и ждать их завершения, фактически не блокируя текущий поток. ForkJoinTask завершается, когда все его подзадачи выполнены, поэтому все вычисления выполняются, когда начальный (root) ForkJoinTask завершено.

Смотрите Oracle - Учебные руководства Java™ - Fork/Join для деталей.

Поскольку все ваши задачи безрезультатны (Runnable), вы должны подкласс RecursiveAction (который сам по себе является подклассом ForkJoinTask). Реализуйте метод compute() и порождают там произвольное количество новых задач, либо вызывая invoke(subtask), invokeAll(subtask1, subtask2, ...) или же subtask.fork() с последующим subtask.join(),

Все вычисления выполняются следующим образом:

MyRecursiveAction task = new MyRecursiveAction(params);
ForkJoinPool pool = new ForkJoinPool(numberOfThreads);
pool.invoke(task); // will block until task is done

К сожалению, преимущества Fork / Join имеют некоторые ограничения, например:

(...) В идеале вычисления должны избегать синхронизированных методов или блоков и должны минимизировать другую блокирующую синхронизацию, кроме присоединения к другим задачам или использования синхронизаторов, таких как Phasers, которые объявлены для взаимодействия с планированием fork/join. Подразделимые задачи также не должны выполнять блокирующий ввод-вывод и в идеале должны иметь доступ к переменным, которые полностью независимы от переменных, к которым обращаются другие выполняющиеся задачи. Эти руководящие принципы неукоснительно соблюдаются, поскольку не разрешают выбрасывать проверенные исключения, такие как IOException. (...)

Для более подробной информации см. API документы ForkJoinTask,

Работать с Future а не с Runnable, Там это Future#isDone метод, который может вам помочь.

Если у вас нет ничего значимого, чтобы вернуться из Callable использовать Callable<Void> а также Future<Void>,

Если вы можете использовать Guava Futures, вы можете использовать Futures.allAsList или же Futures.successfulAsList, Это позволяет вам обернуть несколько Future случаи, когда вы вернулись из ExecutorService в один Future который вы можете проверить, чтобы увидеть, закончил ли он, используя isDone() (или просто get()в этом отношении, если вы хотите заблокировать до завершения).

Другие вопросы по тегам