Как дождаться завершения нескольких потоков?



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



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() методы выхода? Спасибо!

1592   13  

13 ответов:

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

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

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

одним из способов было бы сделать List of Threads, создайте и запустите каждый поток, добавив его в список. Как только все будет запущено, вернитесь в список и вызовите 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-х параллелизм коммунальные услуги) невероятно гибкий, и приведенный выше пример едва ли даже царапает поверхность.

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
   }
}

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

класс ExecutorService предоставляет метод invokeAll это, кажется, делать только то, что вы хотите.

вместо 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. основной поток CountDownLatchDemoawait() пока счет не станет нулевым. После того, как счет стал нулевым, вы получите ниже строки в выводе.

    In Main thread after completion of 1000 threads
    

дополнительная информация со страницы документации oracle

public void await()
           throws InterruptedException

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

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

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

Как предложил Мартин к 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();
        }
    }

}

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

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

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

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

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

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

Я вижу, что выше, вы предлагаете проверки activeCount - отмена МартинvLöwisозабоченность по поводу опроса в сторону на данный момент, у меня есть еще одна проблема с .

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

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

создайте объект потока внутри первого цикла for.

for (int i = 0; i < threads.length; i++) {
     threads[i] = new Thread(new Runnable() {
         public void run() {
             // some code to run in parallel
         }
     });
     threads[i].start();
 }

и то что все здесь говорят.

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

вы можете сделать это с объектом "ThreadGroup" и его параметр activeCount:

в качестве альтернативы CountDownLatch за вы также можете использовать CyclicBarrier например

public class ThreadWaitEx {
    static CyclicBarrier barrier = new CyclicBarrier(100, new Runnable(){
        public void run(){
            System.out.println("clean up job after all tasks are done.");
        }
    });
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(new MyCallable(barrier));
            t.start();
        }       
    }

}    

class MyCallable implements Runnable{
    private CyclicBarrier b = null;
    public MyCallable(CyclicBarrier b){
        this.b = b;
    }
    @Override
    public void run(){
        try {
            //do something
            System.out.println(Thread.currentThread().getName()+" is waiting for barrier after completing his job.");
            b.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }       
}

использовать CyclicBarrier в этом случае барьер.await () должен быть последним оператором, т. е. когда ваш поток завершает свою работу. CyclicBarrier можно использовать снова с его методом reset (). Цитировать документации:

CyclicBarrier поддерживает дополнительную выполняемую команду, которая выполняется один раз в точке барьера после последнего потока в партии прибывает, но до того, как какие-либо потоки будут освобождены. Это действие барьера полезно для обновления общего состояния, прежде чем любая из сторон продолжит.

Comments

    Ничего не найдено.