Как ждать завершения ряда потоков? - PullRequest
98 голосов
/ 10 августа 2009

Как можно просто дождаться завершения всех потоковых процессов? Например, скажем, у меня есть:

public class DoSomethingInAThread implements Runnable{

    public static void main(String[] args) {
        for (int n=0; n<1000; n++) {
            Thread t = new Thread(new DoSomethingInAThread());
            t.start();
        }
        // wait for all threads' run() methods to complete before continuing
    }

    public void run() {
        // do something here
    }


}

Как мне изменить это, чтобы метод main() приостанавливал комментарий, пока не завершатся методы всех потоков run()? Спасибо!

Ответы [ 13 ]

154 голосов
/ 10 августа 2009

Вы помещаете все потоки в массив, запускаете их все, а затем создаете цикл

for(i = 0; i < threads.length; i++)
  threads[i].join();

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

37 голосов
/ 10 августа 2009

Один из способов - сделать List из Thread с, создать и запустить каждый поток, добавляя его в список. После того, как все запущено, вернитесь к списку и назовите join() для каждого. Не имеет значения, в каком порядке завершаются потоки, все, что вам нужно знать, это то, что к моменту завершения второго цикла все потоки будут завершены.

Лучшим подходом является использование ExecutorService и связанных с ним методов:

List<Callable> callables = ... // assemble list of Callables here
                               // Like Runnable but can return a value
ExecutorService execSvc = Executors.newCachedThreadPool();
List<Future<?>> results = execSvc.invokeAll(callables);
// Note: You may not care about the return values, in which case don't
//       bother saving them

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

24 голосов
/ 10 августа 2009
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class DoSomethingInAThread implements Runnable
{
   public static void main(String[] args) throws ExecutionException, InterruptedException
   {
      //limit the number of actual threads
      int poolSize = 10;
      ExecutorService service = Executors.newFixedThreadPool(poolSize);
      List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();

      for (int n = 0; n < 1000; n++)
      {
         Future f = service.submit(new DoSomethingInAThread());
         futures.add(f);
      }

      // wait for all tasks to complete before continuing
      for (Future<Runnable> f : futures)
      {
         f.get();
      }

      //shut down the executor service so that this thread can exit
      service.shutdownNow();
   }

   public void run()
   {
      // do something here
   }
}
8 голосов
/ 09 мая 2016

вместо join(), который является старым API, вы можете использовать CountDownLatch . Я изменил ваш код, как показано ниже, чтобы выполнить ваше требование.

import java.util.concurrent.*;
class DoSomethingInAThread implements Runnable{
    CountDownLatch latch;
    public DoSomethingInAThread(CountDownLatch latch){
        this.latch = latch;
    } 
    public void run() {
        try{
            System.out.println("Do some thing");
            latch.countDown();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

public class CountDownLatchDemo {
    public static void main(String[] args) {
        try{
            CountDownLatch latch = new CountDownLatch(1000);
            for (int n=0; n<1000; n++) {
                Thread t = new Thread(new DoSomethingInAThread(latch));
                t.start();
            }
            latch.await();
            System.out.println("In Main thread after completion of 1000 threads");
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

Объяснение

  1. CountDownLatch был инициализирован с указанным количеством 1000 согласно вашему требованию.

  2. Каждый рабочий поток DoSomethingInAThread будет уменьшать значение CountDownLatch, переданное в конструктор.

  3. Основной поток CountDownLatchDemo await() до тех пор, пока счетчик не станет равным нулю. Как только счет станет равным нулю, вы получите ниже строки в выводе.

    In Main thread after completion of 1000 threads
    

Больше информации на странице документации оракула

public void await()
           throws InterruptedException

Заставляет текущий поток ждать, пока защелка не обратится к нулю, если поток не прерван.

См. Связанный вопрос SE для других опций:

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

8 голосов
/ 10 августа 2009

Избегайте класса Thread и используйте вместо этого более высокие абстракции, представленные в java.util.concurrent

Класс ExecutorService предоставляет метод invokeAll , который, кажется, выполняет то, что вам нужно.

5 голосов
/ 04 марта 2015

Как предположил Мартин К. java.util.concurrent.CountDownLatch кажется лучшим решением для этого. Просто добавив пример для того же

     public class CountDownLatchDemo
{

    public static void main (String[] args)
    {
        int noOfThreads = 5;
        // Declare the count down latch based on the number of threads you need
        // to wait on
        final CountDownLatch executionCompleted = new CountDownLatch(noOfThreads);
        for (int i = 0; i < noOfThreads; i++)
        {
            new Thread()
            {

                @Override
                public void run ()
                {

                    System.out.println("I am executed by :" + Thread.currentThread().getName());
                    try
                    {
                        // Dummy sleep
                        Thread.sleep(3000);
                        // One thread has completed its job
                        executionCompleted.countDown();
                    }
                    catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }.start();
        }

        try
        {
            // Wait till the count down latch opens.In the given case till five
            // times countDown method is invoked
            executionCompleted.await();
            System.out.println("All over");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

}
4 голосов
/ 13 ноября 2014

Рассмотрите возможность использования java.util.concurrent.CountDownLatch. Примеры в javadocs

3 голосов
/ 10 августа 2009

В зависимости от ваших потребностей, вы также можете проверить классы CountDownLatch и CyclicBarrier в пакете java.util.concurrent. Они могут быть полезны, если вы хотите, чтобы ваши потоки ждали друг друга, или если вы хотите более детального контроля над тем, как выполняются ваши потоки (например, ожидая во внутреннем исполнении другого потока, чтобы установить какое-то состояние). Вы также можете использовать CountDownLatch, чтобы сигнализировать обо всех ваших потоках, чтобы они запускались одновременно, вместо того, чтобы запускать их один за другим, когда вы повторяете цикл. Стандартные документы API имеют пример этого, плюс использование другого CountDownLatch для ожидания завершения выполнения всех потоков.

3 голосов
/ 10 августа 2009

Если вы создадите список потоков, вы можете просмотреть их и .join () против каждого, и ваш цикл завершится, когда все потоки будут. Хотя я не пробовал.

http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()

2 голосов
/ 10 августа 2009

Это был бы комментарий, но я пока не могу комментировать.

Мартин К , мне любопытно, как бы вы использовали ThreadGroup. Вы делали это раньше?

Я вижу, что выше, вы предлагаете проверить activeCount - откладывая в сторону Martin v Löwis Забота об опросе на данный момент, у меня есть другая проблема с самим activeCount.

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

Лично я не хотел бы пытаться построить систему на основе оценки. У вас есть другая мысль о том, как это сделать, или я неправильно читаю Javadoc?

...