2009-09-01 16 views
29

Durante el curso de la ejecución de mi programa, se inician varios subprocesos. La cantidad de subprocesos varía según la configuración definida por el usuario, pero todos están ejecutando el mismo método con diferentes variables.Esperando múltiples subprocesos para completar en Java

En algunas situaciones, se requiere una limpieza a mediados de la ejecución, parte de esto es detener todos los hilos, no quiero que se detengan inmediatamente, solo establezco una variable que ellos verifican para que los termine. El problema es que puede pasar hasta 1/2 segundo antes de que el hilo se detenga. Sin embargo, necesito estar seguro de que todos los hilos se hayan detenido antes de que la limpieza continúe. La limpieza se ejecuta desde otro subproceso, así que técnicamente necesito que este subproceso espere a que los otros subprocesos terminen.

He pensado en varias formas de hacerlo, pero todas parecen ser demasiado complejas. Esperaba que hubiera algún método que pueda esperar a que se complete un grupo de hilos. ¿Existe algo como esto?

Gracias.

+0

Posible duplicado de [¿Cómo esperar a que se complete un conjunto de hilos?] (Http://stackoverflow.com/questions/ 1252190/how-to-wait-for-a-set-of-threads-to-complete) –

Respuesta

49

Simplemente se unen uno a uno:

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

(Tendrá que hacer algo con InterruptedException, y es posible que también desee proporcionar un tiempo de espera en caso de que las cosas van mal, pero eso es lo básico idea ...)

+0

Gracias, parece que funciona, tuve que refactorizar mi código un poco para obtener las referencias de hilos en mi método de limpieza, pero creo que está trabajando. Se está ejecutando ahora, solo lleva unos minutos. –

+0

Acaba de completar la prueba. Funcionó perfectamente –

+8

@Jon Skeet, tengo una duda. La línea 'thread.join();' hará que el proceso actual espere hasta que se ejecute thread 'thread', ¿verdad? entonces, lo que sucederá aquí es que el proceso principal llegará a la 2ª línea y esperará a que 'thread' termine su trabajo, luego irá para el próximo' thread' in loop, así que en realidad main esperará hasta que se ejecute el primer thread hijo, entonces ejecutará 'thread.join()' para el siguiente hilo, ¿estoy en lo cierto? – Patriks

8

Definir un método de utilidad (o métodos) a sí mismo:

public static waitFor(Collection<? extends Thread) c) throws InterruptedException { 
    for(Thread t : c) t.join(); 
} 

o puede que tenga una matriz

public static waitFor(Thread[] ts) throws InterruptedException { 
    waitFor(Arrays.asList(ts)); 
} 

Alternativamente, usted podría considerar el uso de un CyclicBarrier en la biblioteca java.util.concurrent implementar un punto arbitrario entre múltiples hilos cita.

14

Si está utilizando Java 1.5 o superior, puede probar CyclicBarrier. Puede pasar la operación de limpieza como su parámetro de constructor, y simplemente llame al barrier.await() en todos los hilos cuando haya una necesidad de limpieza.

+0

Gracias, parece que haría lo que quiero, pero ya había escrito la respuesta de Jon cuando vi esto. –

+2

Una alternativa, si solo lo hace una vez, es usar CountdownLatch. –

5

¿Has visto las clases Executor en java.util.concurrent? Puede ejecutar sus hilos a través de ExecutorService. Le da un objeto único que puede usar para cancelar los hilos o esperar a que se completen.

1

Si controla la creación de los subprocesos (presentación a un ExecutorService) entonces aparece que puede usar un ExecutorCompletionService ver ExecutorCompletionService? Why do need one if we have invokeAll? para obtener varias respuestas allí.

Si no controla la creación de subprocesos, aquí hay un enfoque que le permite unir los hilos "uno por uno mientras terminan" (y saber cuál termina primero, etc.), inspirados en la clase ruby ​​ThreadWait . Básicamente, mediante la actualización de "ver los hilos" que alertan cuando los otros hilos terminan, puede saber cuándo termina el "próximo" hilo de muchos.

que tendría que utilizar algo como esto:

JoinThreads join = new JoinThreads(threads); 
for(int i = 0; i < threads.size(); i++) { 
    Thread justJoined = join.joinNextThread(); 
    System.out.println("Done with a thread, just joined=" + justJoined); 
} 

Y la fuente:

public static class JoinThreads { 
    java.util.concurrent.LinkedBlockingQueue<Thread> doneThreads = 
     new LinkedBlockingQueue<Thread>(); 

    public JoinThreads(List<Thread> threads) { 
    for(Thread t : threads) { 
     final Thread joinThis = t; 
     new Thread(new Runnable() { 
     @Override 
     public void run() { 
      try { 
      joinThis.join(); 
      doneThreads.add(joinThis); 
      } 
      catch (InterruptedException e) { 
      // "should" never get here, since we control this thread and don't call interrupt on it 
      } 
     } 
     }).start(); 
    } 

    } 

    Thread joinNextThread() throws InterruptedException { 
    return doneThreads.take(); 
    } 
} 

La parte buena de esto es que funciona con hilos de Java genéricos, sin modificaciones, cualquier tema se puede unir La advertencia es que requiere un poco de creación de hilo adicional. Además, esta implementación particular "deja hilos atrás" si no llama a joinNextThread() el número completo de veces, y no tiene un método "cerrado", etc. Comente aquí si desea crear una versión más pulida. También podría usar este mismo tipo de patrón con "Futures" en lugar de objetos Thread, etc.

Cuestiones relacionadas