java multithreading countdown countdownlatch

¿Cómo se usa CountDownLatch en Java Multithreading?



(10)

Como se menciona en JavaDoc ( docs.oracle.com/javase/7/docs/api/java/util/concurrent/… ), CountDownLatch es un asistente de sincronización, introducido en Java 5. Aquí la sincronización no significa restringir el acceso a una sección crítica. Pero en lugar de secuenciar acciones de diferentes hilos. El tipo de sincronización logrado a través de CountDownLatch es similar al de Join. Suponga que hay un hilo "M" que necesita esperar que otros hilos de trabajador "T1", "T2", "T3" completen sus tareas. Antes de Java 1.5, la forma en que esto se puede hacer es, M ejecutando el siguiente código

T1.join(); T2.join(); T3.join();

El código anterior se asegura de que el hilo M reanude su trabajo después de que T1, T2, T3 completen su trabajo. T1, T2, T3 pueden completar su trabajo en cualquier orden. Lo mismo se puede lograr a través de CountDownLatch, donde T1, T2, T3 e hilo M comparten el mismo objeto CountDownLatch.
Solicitudes "M": countDownLatch.await();
donde como "T1", "T2", "T3" cuenta countDownLatch.countdown();

Una desventaja del método de unión es que M tiene que saber acerca de T1, T2, T3. Si hay un nuevo hilo de trabajo T4 añadido más tarde, entonces M también debe tenerlo en cuenta. Esto se puede evitar con CountDownLatch. Después de la implementación, la secuencia de acción sería [T1, T2, T3] (el orden de T1, T2, T3 podría ser de todos modos) -> [M]

¿Alguien puede ayudarme a entender qué es Java CountDownLatch y cuándo usarlo?

No tengo una idea muy clara de cómo funciona este programa. Como yo entiendo, los tres hilos comienzan al mismo tiempo y cada Thread llamará a CountDownLatch después de 3000ms. Entonces, la cuenta atrás disminuirá uno por uno. Después de que el cerrojo se vuelva cero, el programa imprime "Completado". Quizás la forma en que entendí es incorrecta.

import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class Processor implements Runnable { private CountDownLatch latch; public Processor(CountDownLatch latch) { this.latch = latch; } public void run() { System.out.println("Started."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); } }

// ------------------------------------------------ -----

public class App { public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0 ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool for(int i=0; i < 3; i++) { executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1 } try { latch.await(); // wait until latch counted down to 0 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Completed."); } }


CoundDownLatch le permite hacer que un subproceso espere hasta que todos los demás subprocesos se hayan completado con su ejecución.

El pseudo código puede ser:

// Main thread starts // Create CountDownLatch for N threads // Create and start N threads // Main thread waits on latch // N threads completes there tasks are returns // Main thread resume execution


De la documentación de CountDownLatch sobre CountDownLatch :

Un asistente de sincronización que permite que uno o más subprocesos esperen hasta que se complete un conjunto de operaciones en otros subprocesos.

Un CountDownLatch se inicializa con un conteo dado. El bloque de métodos de await hasta que el recuento actual llega a cero debido a las invocaciones del método countDown() , después del cual se liberan todos los subprocesos en espera y las invocaciones posteriores de espera vuelven inmediatamente. Este es un fenómeno de una sola vez: el recuento no se puede restablecer.

Un CountDownLatch es una herramienta de sincronización versátil y puede usarse para varios propósitos.

Un CountDownLatch inicializado con un recuento de uno sirve como un simple enclavamiento on / off, o puerta: todos los hilos que invocan esperan a esperar en la puerta hasta que se abre por un hilo que invoca countDown ().

Un CountDownLatch inicializado en N se puede usar para hacer que un hilo espere hasta que N hilos hayan completado alguna acción, o alguna acción se haya completado N veces.

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.

Si el conteo actual es cero, este método regresa inmediatamente.

public void countDown()

Disminuye el recuento del pestillo, liberando todos los hilos de espera si el recuento llega a cero.

Si el recuento actual es mayor que cero, entonces se decrementa. Si el nuevo recuento es cero, todos los subprocesos en espera se vuelven a habilitar para fines de programación de subprocesos.

Explicación de tu ejemplo.

  1. Has configurado el recuento como 3 para la variable de latch

    CountDownLatch latch = new CountDownLatch(3);

  2. Ha pasado este latch compartido a Hilo de trabajo: Processor

  3. Se han Runnable tres instancias Runnable de Processor al executor ExecutorService
  4. El hilo principal ( App ) está esperando que la cuenta se vuelva cero con la declaración siguiente

    latch.await();

  5. Processor hilo del Processor duerme durante 3 segundos y luego disminuye el valor del conteo con latch.countDown()
  6. Process instancia de First Process cambiará el recuento de pestillo como 2 después de que se complete debido a latch.countDown() .

  7. La segunda instancia del Process cambiará el conteo del enganche como 1 después de su finalización debido a latch.countDown() .

  8. Process instancia de Third Process cambiará el recuento de pestillo como 0 después de su finalización debido a latch.countDown() .

  9. La cuenta cero en el pestillo hace que la App principal del hilo salga de la await

  10. El programa de aplicación imprime este resultado ahora: Completed



NikolaB lo explicó muy bien, sin embargo, un ejemplo sería útil para entenderlo, así que aquí hay un ejemplo simple ...

import java.util.concurrent.*; public class CountDownLatchExample { public static class ProcessThread implements Runnable { CountDownLatch latch; long workDuration; String name; public ProcessThread(String name, CountDownLatch latch, long duration){ this.name= name; this.latch = latch; this.workDuration = duration; } public void run() { try { System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds"); Thread.sleep(workDuration); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name+ "completed its works"); //when task finished.. count down the latch count... // basically this is same as calling lock object notify(), and object here is latch latch.countDown(); } } public static void main(String[] args) { // Parent thread creating a latch object CountDownLatch latch = new CountDownLatch(3); new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs System.out.println("waiting for Children processes to complete...."); try { //current thread will get notified if all chidren''s are done // and thread will resume from wait() mode. latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("All Process Completed...."); System.out.println("Parent Thread Resuming work...."); } }


Sí, entendiste correctamente. CountDownLatch funciona en principio de cierre, el hilo principal esperará hasta que la compuerta esté abierta. Un subproceso espera n subprocesos, especificados al crear CountDownLatch .

Cualquier hilo, generalmente el hilo principal de la aplicación, que llama a CountDownLatch.await() esperará hasta que el contador llegue a cero o se interrumpa por otro hilo. Todos los demás hilos deben contar hacia atrás llamando a CountDownLatch.countDown() una vez que estén completos o listos.

Tan pronto como el conteo llega a cero, el hilo de espera continúa. Una de las desventajas / ventajas de CountDownLatch es que no es reutilizable: una vez que el conteo llega a cero no puede usar CountDownLatch .

Editar:

Use CountDownLatch cuando un hilo (como el hilo principal) requiere que espere uno o más hilos para completar, antes de que pueda continuar el proceso.

Un ejemplo clásico del uso de CountDownLatch en Java es una aplicación Java central del lado del servidor que utiliza la arquitectura de servicios, donde múltiples hilos son provistos por múltiples hilos y la aplicación no puede comenzar a procesar hasta que todos los servicios hayan comenzado con éxito.

La pregunta de PS OP tiene un ejemplo bastante sencillo, así que no incluí una.


Se usa cuando queremos esperar a que más de un hilo complete su tarea. Es similar a unirse en hilos.

Donde podemos usar CountDownLatch

Considere un escenario donde tenemos un requisito donde tenemos tres hilos "A", "B" y "C" y queremos comenzar el hilo "C" solo cuando los hilos "A" y "B" completan o completan parcialmente su tarea.

Se puede aplicar al escenario de TI del mundo real

Considere un escenario en el que el administrador divide los módulos entre los equipos de desarrollo (A y B) y desea asignarlo al equipo de control de calidad para que lo pruebe solo cuando ambos equipos completen su tarea.

public class Manager { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(2); MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA"); MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB"); teamDevA.start(); teamDevB.start(); countDownLatch.await(); MyQATeam qa = new MyQATeam(); qa.start(); } } class MyDevTeam extends Thread { CountDownLatch countDownLatch; public MyDevTeam (CountDownLatch countDownLatch, String name) { super(name); this.countDownLatch = countDownLatch; } @Override public void run() { System.out.println("Task assigned to development team " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("Task finished by development team Thread.currentThread().getName()); this.countDownLatch.countDown(); } } class MyQATeam extends Thread { @Override public void run() { System.out.println("Task assigned to QA team"); try { Thread.sleep(2000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("Task finished by QA team"); } }

La salida del código anterior será:

Tarea asignada al equipo de desarrollo devB

Tarea asignada al equipo de desarrollo devA

Tarea terminada por el equipo de desarrollo devB

Tarea terminada por el equipo de desarrollo devA

Tarea asignada al equipo de control de calidad

Tarea terminada por el equipo de QA

Aquí el método await () espera a que el indicador countdownlatch se convierta en 0, y el método countDown () disminuye el indicador countdownlatch en 1.

Limitación de JOIN: El ejemplo anterior también se puede lograr con JOIN, pero JOIN no se puede usar en dos escenarios:

  1. Cuando utilizamos ExecutorService en lugar de la clase Thread para crear subprocesos.
  2. Modifique el ejemplo anterior donde el administrador desea transferir el código al equipo de control de calidad tan pronto como el desarrollo complete su tarea del 80%. Significa que CountDownLatch nos permite modificar la implementación que puede usarse para esperar otro hilo para su ejecución parcial.

Si agrega un poco de depuración después de su llamada a latch.countDown (), esto puede ayudarlo a comprender mejor su comportamiento.

latch.countDown(); System.out.println("DONE "+this.latch); // Add this debug

La salida mostrará que el recuento está decrementado. Este ''recuento'' es efectivamente el número de tareas Ejecutables (objetos del procesador) que ha iniciado contra el que countDown () no se ha invocado y, por lo tanto, está bloqueado el hilo principal en su llamada a latch.await ().

DONE java.util.concurrent.CountDownLatch@70e69696[Count = 2] DONE java.util.concurrent.CountDownLatch@70e69696[Count = 1] DONE java.util.concurrent.CountDownLatch@70e69696[Count = 0]


Un buen ejemplo de cuándo usar algo como esto es con Java Simple Serial Connector, accediendo a puertos seriales. Por lo general, escribirás algo en el puerto, y de manera asincrónica, en otro hilo, el dispositivo responderá en un SerialPortEventListener. Normalmente, querrá pausar después de escribir en el puerto para esperar la respuesta. Manejar manualmente los bloqueos de subprocesos para este escenario es extremadamente complicado, pero usar Countdownlatch es fácil. Antes de ir pensando que puedes hacerlo de otra manera, ¡ten cuidado con las condiciones de carrera en las que nunca pensaste!

Pseudocódigo:

CountDownLatch latch; void writeData() { latch = new CountDownLatch(1); serialPort.writeBytes(sb.toString().getBytes()) try { latch.await(4, TimeUnit.SECONDS); } catch (InterruptedException e) { } } class SerialPortReader implements SerialPortEventListener { public void serialEvent(SerialPortEvent event) { if(event.isRXCHAR()){//If data is available byte buffer[] = serialPort.readBytes(event.getEventValue()); latch.countDown(); } } }


CountDownLatch en Java es un tipo de sincronizador que permite que un Thread espere uno o más Thread antes de que comience el procesamiento.

CountDownLatch funciona según el principio de enganche, el hilo esperará hasta que la puerta esté abierta. Un subproceso espera n cantidad de subprocesos especificados al crear CountDownLatch .

por ejemplo, final CountDownLatch latch = new CountDownLatch(3);

Aquí establecemos el contador a 3.

Cualquier hilo, generalmente el hilo principal de la aplicación, que llama a CountDownLatch.await() esperará hasta que el contador llegue a cero o se interrumpa por otro Thread . Todos los demás hilos deben hacer una cuenta regresiva llamando a CountDownLatch.countDown() una vez que estén completos o listos para el trabajo. tan pronto como el conteo llega a cero, el Thread espera comienza a ejecutarse.

Aquí el recuento se reduce por el método CountDownLatch.countDown() .

El Thread que llama al método await() esperará hasta que el recuento inicial llegue a cero.

Para hacer el recuento cero, otros subprocesos necesitan llamar al método countDown() . Una vez que el conteo llega a cero, el hilo que invocó el método await() se reanudará (comenzará su ejecución).

La desventaja de CountDownLatch es que no es reutilizable: una vez que el recuento se vuelve cero ya no se puede usar.