ждать, пока все потоки не закончат свою работу в Java - PullRequest
79 голосов
/ 29 октября 2011

Я пишу приложение, в котором есть 5 потоков, которые одновременно получают некоторую информацию из Интернета и заполняют 5 различных полей в классе буфера.
Мне нужно проверить данные буфера и сохранить их в базе данных, когда все потоки закончили свою работу.job.
Как я могу это сделать (получить уведомление, когда все потоки закончили свою работу)?

Ответы [ 16 ]

102 голосов
/ 29 октября 2011

Я использую ExecutorService для управления пулами потоков.

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
45 голосов
/ 29 октября 2011

Вы можете join в темах. Блоки объединения, пока поток не завершится.

for (Thread thread : threads) {
    thread.join();
}

Обратите внимание, что join бросает InterruptedException. Вам придется решить, что делать, если это произойдет (например, попытайтесь отменить другие потоки, чтобы предотвратить ненужную работу).

20 голосов
/ 22 апреля 2016

Посмотрите на различные решения.

  1. join() API был представлен в ранних версиях Java.Некоторые хорошие альтернативы доступны с этим параллельным пакетом начиная с выпуска JDK 1.5.

  2. ExecutorService # invokeAll ()

    Выполняет заданные задачи, возвращая список Фьючерсов, содержащих их статус и результаты, когда все завершено.

    См. Этот связанный вопрос SE для примера кода:

    Как использовать invokeAll (), чтобы все пулы потоков выполняли свою задачу?

  3. CountDownLatch

    Средство синхронизацииэто позволяет одному или нескольким потокам дождаться завершения набора операций, выполняемых в других потоках.

    A CountDownLatch инициализируется с заданным количеством.Методы await блокируются до тех пор, пока текущий счетчик не достигнет нуля из-за вызовов метода countDown(), после чего все ожидающие потоки освобождаются и любые последующие вызовы await немедленно возвращаются.Это одноразовое явление - счет не может быть сброшен.Если вам нужна версия, которая сбрасывает счет, рассмотрите возможность использования CyclicBarrier .

    См. Этот вопрос для использования CountDownLatch

    Какждать потока, который порождает свой собственный поток?

  4. ForkJoinPool или newWorkStealingPool () in Executors

  5. Итерация по всем Будущим объектам, созданным после отправки в ExecutorService

10 голосов
/ 29 октября 2011

Помимо Thread.join(), предложенного другими, Java 5 представила среду исполнения. Там вы не работаете с Thread объектами. Вместо этого вы отправляете свои объекты Callable или Runnable исполнителю. Есть специальный исполнитель, который предназначен для выполнения нескольких задач и выдачи их результатов не по порядку. Это ExecutorCompletionService:

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

Затем вы можете повторно вызывать take(), пока не останется больше Future<?> объектов для возврата, что означает, что все они завершены.


Еще одна вещь, которая может иметь отношение к вашему сценарию, это CyclicBarrier.

Средство синхронизации, которое позволяет всем потокам ожидать друг друга, чтобы достичь общей барьерной точки. CyclicBarriers полезны в программах, включающих группу потоков фиксированного размера, которые иногда должны ждать друг друга. Барьер называется циклическим, поскольку его можно использовать повторно после освобождения ожидающих потоков.

9 голосов
/ 29 октября 2011

Другой возможностью является объект CountDownLatch, который полезен для простых ситуаций: так как вы заранее знаете количество потоков, вы инициализируете его соответствующим количеством и передаете ссылку на объектк каждому потоку.
По завершении своей задачи каждый поток вызывает CountDownLatch.countDown(), который уменьшает внутренний счетчик.Основной поток после запуска всех остальных должен выполнить блокирующий вызов CountDownLatch.await().Он будет выпущен, как только внутренний счетчик достигнет 0.

Обратите внимание, что с этим объектом можно также выбросить InterruptedException.

8 голосов
/ 29 октября 2011

Вы делаете

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

После этого цикла for вы можете быть уверены, что все потоки завершили свою работу.

4 голосов
/ 29 октября 2011

Сохраните объекты Thread в некоторой коллекции (например, List или Set), затем выполните цикл по коллекции после запуска потоков и вызовите join () в Threads.

2 голосов
/ 24 января 2013

Хотя это не относится к проблеме OP, если вы заинтересованы в синхронизации (точнее, рандеву) только с одним потоком, вы можете использовать Exchanger

В моем случаеМне нужно было приостановить родительский поток, пока дочерний поток не сделал что-то, например, завершил свою инициализацию.A CountDownLatch также хорошо работает.

2 голосов
/ 29 октября 2011

Вы можете использовать Threadf # join метод для этой цели.

1 голос
/ 20 июня 2016

У меня была похожая проблема, и я в конечном итоге использовал Java 8 parallelStream.

requestList.parallelStream().forEach(req -> makeRequest(req));

Это супер просто и читабельно. За кулисами используется пул форк-соединений JVM по умолчанию, что означает, что он будет ждать завершения всех потоков, прежде чем продолжить. Для моего случая это было аккуратное решение, потому что это был единственный параллельный поток в моем приложении. Если у вас одновременно запущено несколько параллельных потоков, прочитайте ссылку ниже.

Подробнее о параллельных потоках здесь .

...