¿Cómo esperar a que se completen varios hilos?

¿Cuál es una forma de simplemente esperar a que termine todo el proceso de subprocesos? Por ejemplo, digamos que tengo:

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

¿Cómo modifico esto para que el método main() haga una pausa en el comentario hasta que todos los métodos de run() los hilos run() salgan? ¡Gracias!

Coloca todos los hilos en una matriz, los inicia todos, y luego tiene un ciclo

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

Cada unión se bloqueará hasta que el hilo respectivo se haya completado. Los hilos pueden completarse en un orden diferente al que se une a ellos, pero eso no es un problema: cuando el ciclo sale, todos los hilos se completan.

Una forma sería crear una List de hilos, crear y ejecutar cada hilo, al tiempo que se agrega a la lista. Una vez que se haya iniciado todo, vuelva a recorrer la lista y llame a join() en cada uno. No importa en qué orden terminen los subprocesos, todo lo que necesita saber es que para cuando el segundo ciclo termine de ejecutarse, cada subproceso habrá finalizado.

Un mejor enfoque es usar un ExecutorService y sus métodos asociados:

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

Usar un ExecutorService (y todas las cosas nuevas de las utilidades de simultaneidad de Java 5) es increíblemente flexible, y el ejemplo anterior apenas araña la superficie.

 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> futures = new ArrayList>(); 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 f : futures) { f.get(); } //shut down the executor service so that this thread can exit service.shutdownNow(); } public void run() { // do something here } } 

Evite la clase Thread por completo y en su lugar use las abstracciones más altas provistas en java.util.concurrent

La clase ExecutorService proporciona el método invokeAll que parece hacer justo lo que usted desea.

en lugar de join() , que es una antigua API, puede usar CountDownLatch . He modificado su código de la siguiente manera para cumplir con sus requisitos.

 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(); } } } 

Explicación

  1. CountDownLatch se ha inicializado con el recuento dado 1000 según su requisito.

  2. Cada subproceso de trabajo DoSomethingInAThread disminuirá el CountDownLatch , que se ha pasado en el constructor.

  3. El hilo principal CountDownLatchDemo await() hasta que el recuento se haya convertido en cero. Una vez que el recuento se haya convertido en cero, obtendrá una línea inferior en la salida.

     In Main thread after completion of 1000 threads 

Más información de la página de documentación de Oracle

 public void await() throws InterruptedException 

Hace que el hilo actual espere hasta que el enganche cuente hacia abajo a cero, a menos que el hilo se interrumpa.

Consulte la pregunta SE relacionada para otras opciones:

espere hasta que todos los hilos terminen su trabajo en java

Como Martin K sugirió java.util.concurrent.CountDownLatch parece ser una mejor solución para esto. Solo agregando un ejemplo para el mismo

  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(); } } } 

Dependiendo de sus necesidades, es posible que también desee consultar las clases CountDownLatch y CyclicBarrier en el paquete java.util.concurrent. Pueden ser útiles si desea que sus subprocesos esperen entre sí, o si desea un control más detallado sobre la forma en que se ejecutan sus subprocesos (por ejemplo, esperando en su ejecución interna para que otro subproceso establezca algún estado). También podría usar un CountDownLatch para indicarle a todos sus hilos que comiencen al mismo tiempo, en lugar de iniciarlos uno por uno a medida que recorre el ciclo. Los documentos API estándar tienen un ejemplo de esto, además de usar otro CountDownLatch para esperar a que todos los hilos completen su ejecución.

Considere usar java.util.concurrent.CountDownLatch . Ejemplos en javadocs

Si hace una lista de los hilos, puede recorrerlos y .join () contra cada uno, y su bucle terminará cuando todos los hilos tengan. No lo he intentado sin embargo.

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

Esto sería un comentario, pero no puedo hacer comentarios todavía.

Martin K , tengo curiosidad acerca de cómo ThreadGroup . ¿Lo has hecho antes?

Veo que arriba, sugieres que compruebes activeCount : dejando de lado la preocupación de Martin v Löwis por las encuestas, por el momento, tengo otra preocupación con activeCount .

Advertencia: No he intentado usar esto, así que no soy experto en el tema, pero según los javadocs , devuelve una estimación del número de hilos activos.

Personalmente, detestaría intentar y construir un sistema en una estimación. ¿Tienes otro pensamiento sobre cómo hacerlo, o estoy malinterpretando el javadoc?

Cree el objeto thread dentro del primer ciclo 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(); } 

Y entonces lo que todos aquí están diciendo.

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

Puedes hacerlo con el objeto “ThreadGroup” y su parámetro activeCount :

Como alternativa a CountDownLatch , también puede usar CyclicBarrier, por ejemplo

 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(); } } } 

Para usar CyclicBarrier en este caso, barrier.await () debería ser la última statement, es decir, cuando el hilo termina con su trabajo. CyclicBarrier se puede usar nuevamente con su método reset (). Para citar javadocs:

Un CyclicBarrier admite un comando Ejecutable opcional que se ejecuta una vez por punto de barrera, después de que llega el último subproceso en la fiesta, pero antes de que se libere cualquier subproceso. Esta acción de barrera es útil para actualizar el estado compartido antes de que continúe cualquiera de las partes.