Дождитесь, пока все потоки закончат работу в java

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

Ответ 1

Подход, который я использую, заключается в использовании 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.

Ответ 2

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

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

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

Ответ 3

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

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

  2. ExecutorService # invokeAll()

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

    Обратитесь к этому связанному вопросу SE для примера кода:

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

  3. CountDownLatch

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

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

    Обратитесь к этому вопросу для использования CountDownLatch

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

  4. ForkJoinPool или newWorkStealingPool() в исполнителях

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

Ответ 4

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

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

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


Еще одна вещь, которая может быть релевантной, зависит от вашего сценария: CyclicBarrier.

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

Ответ 5

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

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

Ответ 6

Вы делаете

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

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

Ответ 7

Храните объекты Thread в какой-либо коллекции (например, List или Set), затем прокручивайте коллекцию после запуска потоков и вызывайте join() в разделе "Темы".

Ответ 8

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

Ответ 9

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

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

Ответ 10

Служба-исполнитель может использоваться для управления несколькими потоками, включая статус и завершение. См. http://programmingexamples.wikidot.com/executorservice

Ответ 11

попробуйте это, будет работать.

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }

Ответ 12

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

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

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

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

Ответ 13

Существующие ответы могут join() каждому потоку.

Но есть несколько способов получить массив/список потоков:

  • Добавьте тему в список при создании.
  • Используйте ThreadGroup для управления потоками.

Следующий код будет использовать подход ThreadGruop. Сначала создается группа, затем при создании каждого потока указывается группа в конструкторе, позже можно получить массив потоков через ThreadGroup.enumerate()


Код

SyncBlockLearn.java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

Основной поток будет ожидать завершения всех потоков в группе.

Ответ 14

Я создал небольшой вспомогательный метод для ожидания нескольких потоков:

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Ответ 15

Подождите/заблокируйте главу потока, пока некоторые другие потоки не завершат свою работу.

Как сказал @Ravindra babu этого можно достичь разными способами, но на примерах.

  • java.lang.Thread. join() Начиная с: 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • java.util.concurrent.CountDownLatch С тех пор: 1.5

    • .countDown() "Уменьшает количество групп защелок.
    • .await() "Блок методов ожидания, пока текущий счетчик не достигнет нуля.

    Если вы создали latchGroupCount = 4 то countDown() должен быть вызван 4 раза, чтобы сделать счетчик 0. Итак, await() освободит блокирующие потоки.

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

Пример кода резьбового класса LatchTask. Чтобы проверить подход, используйте joiningThreads(); и latchThreads(); из основного метода.

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • CyclicBarriers Средство синхронизации, которое позволяет всем потокам ожидать друг друга, чтобы достичь общей точки барьера. CyclicBarriers полезны в программах, включающих группу потоков фиксированного размера, которые должны время от времени ожидать друг друга. Барьер называется циклическим, поскольку его можно использовать повторно после освобождения ожидающих потоков.
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    
    Например, обратитесь к этому классу Concurrent_ParallelNotifyies.

  • Среда выполнения: мы можем использовать ExecutorService для создания пула потоков и отслеживать ход выполнения асинхронных задач с помощью Future.

    • submit(Runnable), submit(Callable) которые возвращают Future Object. Используя future.get() мы можем блокировать основной поток, пока рабочие потоки не завершат свою работу.

    • invokeAll(...) - возвращает список объектов Future, с помощью которого вы можете получить результаты выполнения каждого Callable.

Найдите пример использования интерфейсов Runnable, Callable с платформой Executor.


@Смотрите также

Ответ 16

Используйте это в своем основном потоке: while (! executor.isTerminated()); Поместите эту строку кода после запуска всех потоков из службы исполнителя. Это запустит основной поток после завершения всех потоков, запущенных исполнителями. Обязательно вызывайте executor.shutdown(); перед вышеуказанным циклом.