Wie wird CountDownLatch in Java Multithreading verwendet?

Kann mir jemand helfen zu verstehen, was Java CountDownLatch ist und wann es zu benutzen ist?

Ich habe keine klare Vorstellung davon, wie dieses Programm funktioniert. Wie ich weiß, beginnen alle drei Threads gleichzeitig und jeder Thread ruft CountDownLatch nach 3000ms auf. Also Countdown wird eins nach dem anderen dekrementieren. Nachdem der Signalspeicher auf Null gesetzt wurde, druckt das Programm “Completed”. Vielleicht ist das, was ich verstanden habe, falsch.

 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."); } } 

   

Ja, du hast es richtig verstanden. CountDownLatch arbeitet im Latch-Prinzip, der Haupt-Thread wartet, bis das Gate offen ist. Ein Thread wartet auf n Threads, die beim Erstellen des CountDownLatch .

Jeder Thread, normalerweise der Hauptthread der Anwendung, der CountDownLatch.await() aufruft, wird warten, bis der Zählwert Null erreicht oder von einem anderen Thread unterbrochen wird. Alle anderen Threads müssen durch CountDownLatch.countDown() sobald sie abgeschlossen oder bereit sind.

Sobald die Zählung Null erreicht, wird der wartende Thread fortgesetzt. Einer der Nachteile / Vorteile von CountDownLatch ist, dass es nicht wiederverwendbar ist: Sobald die Anzahl Null erreicht, können Sie CountDownLatch nicht mehr verwenden.

Bearbeiten:

Verwenden Sie CountDownLatch wenn ein Thread (wie der Hauptthread) warten muss, bis ein oder mehrere Threads abgeschlossen sind, bevor die Verarbeitung fortgesetzt werden kann.

Ein klassisches Beispiel für die Verwendung von CountDownLatch in Java ist eine serverseitige core-Java-Anwendung, die eine CountDownLatch verwendet, in der mehrere Dienste von mehreren Threads bereitgestellt werden und die Anwendung die Verarbeitung erst beginnen kann, nachdem alle Dienste erfolgreich gestartet wurden.

PS OP’s Frage hat ein ziemlich einfaches Beispiel, also habe ich keins aufgenommen.

CountDownLatch in Java ist eine Art von Synchronizer, der es einem Thread erlaubt, auf einen oder mehrere Thread zu warten, bevor es mit der Verarbeitung beginnt.

CountDownLatch arbeitet nach dem Latch-Prinzip, Thread wartet, bis das Gate geöffnet ist. Ein Thread wartet auf n Anzahl von Threads, die beim Erstellen von CountDownLatch .

zB final CountDownLatch latch = new CountDownLatch(3);

Hier setzen wir den Zähler auf 3.

Jeder Thread, normalerweise der Hauptthread der Anwendung, der CountDownLatch.await() aufruft, wartet, bis der Zählwert Null erreicht oder von einem anderen Thread unterbrochen wird. Alle anderen Threads müssen CountDownLatch.countDown() , indem sie CountDownLatch.countDown() aufrufen, sobald sie abgeschlossen oder für den Job bereit sind. Sobald die Zählung null erreicht, beginnt der wartende Thread laufen.

Hier wird die Anzahl durch die CountDownLatch.countDown() Methode dekrementiert.

Der Thread der die await() -Methode aufruft, wartet, bis die anfängliche Zählung Null erreicht.

Um die Zählung Null zu machen, müssen andere Threads die countDown() Methode countDown() . Sobald der Zähler auf Null gesetzt ist, wird der Thread, der await() Methode ” await() aufgerufen hat, fortgesetzt (startet seine Ausführung).

Der Nachteil von CountDownLatch ist, dass es nicht wiederverwendbar ist: Sobald der Zählerstand Null wird, ist es nicht mehr verwendbar.

NikolaB hat es sehr gut erklärt, aber Beispiel wäre hilfreich zu verstehen, also hier ist ein einfaches Beispiel …

  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...."); } } 

Es wird verwendet, wenn wir auf mehr als einen Thread warten möchten, um seine Aufgabe abzuschließen. Es ist ähnlich, Threads beizutreten.

Wo können wir CountDownLatch verwenden

Stellen Sie sich ein Szenario vor, in dem drei Threads “A”, “B” und “C” vorhanden sind und wir den Thread “C” nur dann starten möchten, wenn die Threads “A” und “B” ihre Aufgabe abschließen oder teilweise abschließen.

Es kann auf reale IT-Szenarien angewendet werden

Stellen Sie sich ein Szenario vor, in dem der Manager Module zwischen den Entwicklungsteams (A und B) teilt und er das QA-Team nur dann zum Testen zuweisen möchte, wenn beide Teams ihre Aufgabe abgeschlossen haben.

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

Ausgabe des obigen Codes wird sein:

Aufgabe, die dem Entwicklungsteam devB zugewiesen wurde

Aufgabe, die dem Entwicklungsteam DevA zugewiesen wurde

Aufgabe vom Entwicklungsteam devB beendet

Aufgabe vom Entwicklungsteam DevA abgeschlossen

Aufgabe, die dem QA-Team zugewiesen wurde

Aufgabe vom QA-Team beendet

Hier wartet die Methode await () darauf , dass das Flag countdownlatch 0 wird und die Methode countDown () countdownlatch Flag um 1 dekrementiert.

Einschränkung von JOIN: Das obige Beispiel kann auch mit JOIN erreicht werden, aber JOIN kann nicht in zwei Szenarien verwendet werden:

  1. Wenn wir ExecutorService anstelle der Thread-class verwenden, um Threads zu erstellen.
  2. Ändern Sie das obige Beispiel, in dem der Manager Code an das QA-Team übergeben möchte, sobald die 80% -Aufgabe abgeschlossen ist. Es bedeutet, dass CountDownLatch uns erlaubt, die Implementierung zu ändern, die verwendet werden kann, um auf einen anderen Thread für ihre teilweise Ausführung zu warten.

Ein gutes Beispiel dafür, wie man etwas verwendet, ist der Java Simple Serial Connector, der auf serielle Ports zugreift. Normalerweise schreiben Sie etwas in den Port und asynchron reactjs das Gerät in einem anderen Thread auf einen SerialPortEventListener. In der Regel sollten Sie nach dem Schreiben in den Anschluss pausieren, um auf die Antwort zu warten. Die manuelle Handhabung der Thread-Sperren für dieses Szenario ist extrem schwierig, aber die Verwendung von Countdownlatch ist einfach. Bevor du nachdenkst, kannst du es auf eine andere Art und Weise tun, sei vorsichtig mit Rassenbedingungen, an die du nie gedacht hast !!

Pseudocode:


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

Mit CoundDownLatch können Sie einen Thread warten lassen, bis alle anderen Threads mit ihrer Ausführung fertig sind.

Pseudocode kann sein:

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

Wenn Sie nach Ihrem Aufruf von latch.countDown () etwas debuggen, kann dies Ihnen helfen, das Verhalten besser zu verstehen.

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

Die Ausgabe zeigt, dass der Count dekrementiert wird. Diese ‘Anzahl’ ist effektiv die Anzahl der ausführbaren Tasks (processorobjekte), gegen die countDown () nicht aufgerufen wurde, und daher wird der Hauptthread bei seinem Aufruf von latch.await () blockiert.

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

Aus der oracledokumentation über CountDownLatch :

Eine Synchronisierungshilfe, die es einem oder mehreren Threads erlaubt, zu warten, bis ein Satz von Operationen in anderen Threads ausgeführt wird.

Ein CountDownLatch wird mit einer bestimmten Anzahl initialisiert. Die await Methoden blockieren, bis die aktuelle Zählung aufgrund von Aufrufen der countDown() -Methode null erreicht, woraufhin alle wartenden Threads freigegeben werden und alle nachfolgenden countDown() warte sofort zurückkehren. Dies ist ein One-Shot-Phänomen – die Zählung kann nicht zurückgesetzt werden.

Ein CountDownLatch ist ein vielseitiges Synchronisationstool und kann für verschiedene Zwecke verwendet werden.

Ein CountDownLatch mit einer Anzahl von 1 initialisiert wurde, dient als einfaches Ein / Aus-Latch oder Gate: alle Threads, die aufgerufen werden, warten auf das Gate, bis es von einem Thread geöffnet wird, der countDown () aufruft.

Ein mit N initialisierter CountDownLatch kann verwendet werden, um einen Thread warten zu lassen, bis N Threads eine Aktion abgeschlossen haben oder eine Aktion N-mal abgeschlossen wurde.

 public void await() throws InterruptedException 

Bewirkt, dass der aktuelle Thread wartet, bis der Latch auf Null zurückgezählt hat, sofern der Thread nicht unterbrochen wird.

Wenn die aktuelle Anzahl Null ist, wird diese Methode sofort zurückgegeben.

 public void countDown() 

Verringert den Wert des Latch und gibt alle wartenden Threads frei, wenn der Zählerstand Null erreicht.

Wenn der aktuelle Zählwert größer als Null ist, wird er dekrementiert. Wenn der neue Zählwert null ist, werden alle wartenden Threads für Thread-Zeitplanungszwecke wieder aktiviert.

Erklärung Ihres Beispiels

  1. Sie haben den Wert 3 für die latch Variable festgelegt

     CountDownLatch latch = new CountDownLatch(3); 
  2. Sie haben dieses freigegebene latch an den Worker-Thread übergeben: Processor

  3. Drei Runnable Instanzen von Processor wurden an den ExecutorService executor
  4. Der Haupt-Thread ( App ) wartet darauf, dass die Zählung mit der folgenden statement zu Null wird

      latch.await(); 
  5. Processor Thread schläft für 3 Sekunden und dekrementiert dann den Zählwert mit latch.countDown()
  6. Die erste Process ändert die Latch-Anzahl nach der Fertigstellung aufgrund von latch.countDown() .

  7. Die zweite Process ändert den Latch-Zählerstand nach der Fertigstellung aufgrund von latch.countDown() als 1.

  8. Die dritte Process ändert die Latch-Anzahl nach der Fertigstellung aufgrund von latch.countDown() als 0.

  9. Zero Count auf Latch bewirkt, dass der Haupt-Thread- App aus dem await

  10. Das App-Programm druckt jetzt diese Ausgabe: Completed

Wie in JavaDoc ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html ) erwähnt, ist CountDownLatch eine Synchronisationshilfe, eingeführt in Java 5. Hier ist die Synchronisation nicht bedeutet, den Zugriff auf einen kritischen Bereich zu beschränken. Vielmehr Sequenzierungsaktionen verschiedener Threads. Die Art der Synchronisation, die durch CountDownLatch erreicht wird, ist ähnlich der von Join. Angenommen, es gibt einen Thread “M”, der auf andere Worker-Threads “T1”, “T2”, “T3” warten muss, um seine Aufgaben abzuschließen. Vor Java 1.5 kann M folgendermaßen ausgeführt werden

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

Der obige Code stellt sicher, dass Thread M seine Arbeit wieder aufnimmt, nachdem T1, T2, T3 seine Arbeit abgeschlossen hat. T1, T2, T3 können ihre Arbeit in beliebiger Reihenfolge abschließen. Dasselbe kann durch CountDownLatch erreicht werden, wobei T1, T2, T3 und Thread M dasselbe CountDownLatch-Objekt teilen.
“M” Anfragen: countDownLatch.await();
wo als “T1”, “T2”, “T3” countDownLatch.countdown();

Ein Nachteil der Join-Methode ist, dass M über T1, T2, T3 wissen muss. Wenn später ein neuer Worker-Thread T4 hinzugefügt wird, muss M dies ebenfalls beachten. Dies kann mit CountDownLatch vermieden werden. Nach der Implementierung wäre die Reihenfolge der Aktion [T1, T2, T3] (die Reihenfolge von T1, T2, T3 könnte sowieso sein) -> [M]

Bestes Echtzeit-Beispiel für countDownLatch, das in diesem Link CountDownLatchExample erläutert wird