Im wirklichen Leben begegnen wir oft auf eine solche Situation. Bevor wir eine Aktivität ausführen, müssen wir warten, bis wir alle Menschen haben, bevor wir anfangen. Zum Beispiel müssen Sie beim Essen warten, bis die ganze Familie auf dem Sitz ist, bevor Sie auf Reisen fahren, bis alle hier sind, bevor Sie sich auf den Weg machen, und wenn die Athleten auf dem Platz sind, müssen Sie warten, bis die Athleten auf dem Platz sind, bevor Sie anfangen. Das JUC -Paket bietet uns eine Synchronisierungs -Toolklasse, die diese Art von Szenarien gut simulieren kann, was die Cyclicbarrier -Klasse ist. Unter Verwendung der CyclicBarrier -Klasse kann eine Gruppe von Threads implementiert werden, um aufeinander zu warten und dann nachfolgende Operationen auszuführen, wenn alle Threads einen bestimmten Barrierpunkt erreichen. Die folgende Abbildung zeigt diesen Prozess.
Es gibt einen Zähler in der Cyclicbarrier -Klasse. Jeder Thread ruft die wartende Methode auf, um sich zu blockieren, wenn er den Barrierepunkt erreicht. Zu diesem Zeitpunkt wird der Zähler durch 1. Wenn der Zähler auf 0 verringert wird, werden alle Threads blockiert, indem das Wartenverfahren aufgerufen wird. Dies ist das Prinzip der Implementierung einer Reihe von Threads, die aufeinander warten. Schauen wir uns zunächst an, welche Mitgliedervariablen Cyclicbarrier haben.
// Synchronous Operation Lock Private Final Reentrantlock Lock = new Reentrantlock (); // Thread Interceptor Private Final Bedingung Trip = Lock.NewCondition (); // Anzahl der Threads, die jedes Mal private endgültige Int -Parteien abgezeigt werden; // Aufgaben, die vor dem Ersatz für die private Intk -Störung der privaten Intk -Störung der privaten Intat -Struktur der privaten Intk -Struktur der privaten Generation der privaten Generation der Feide -Struktur der privaten Generation (). GenerationPrivate statische Klassengenerierung {boolean kaputt = false;}Alle Mitgliedsvariablen von Cyclicbarrier sind oben veröffentlicht. Sie können sehen, dass die internen Cyclicbarrier-Blocks durch eine bedingte Warteschlangenreise blockiert und zwei INT-Typ-Variablenparteien und zählen. Die Parteien repräsentieren die Anzahl der Threads, die jedes Mal abgefangen wurden, und dieser Wert wird während der Konstruktion zugewiesen. Die Anzahl ist ein interner Zähler, sein Anfangswert ist der gleiche wie Parteien und verringert dann mit jedem Anruf der Warteverfahren um 1, bis sie auf 0 reduziert wird und alle Threads aufweckt. CyclicBarrier verfügt über eine statische interne Klassengeneration, und die Objekte dieser Klasse repräsentieren die aktuelle Generation des Zauns, genau wie das Spiel beim Spielen beim Spielen und verwendete das Warten von Schleifen. BarrierCommand gibt die vor dem Ersatz ausgeführte Aufgabe an. Wenn die Anzahl auf 0 reduziert wird, bedeutet dies, dass das Spiel vorbei ist und auf das nächste Spiel übertragen werden muss. Alle blockierenden Threads werden geweckt, bevor sie zum nächsten Spiel gehen. Bevor Sie alle Threads aufwachen, können Sie Ihre eigenen Aufgaben ausführen, indem Sie eine Barrieremanie angeben. Als nächstes werfen wir einen Blick auf seinen Konstruktor.
// Konstruktor 1Public Cyclicbarrier (int Partys, Runnable Barrieraction) {if (Parteien <= 0) werfen neue illegalArgumentException (); this.Parties = Partys; this.count = Partys; this.BarrierCommand = Barrieraction;} // Konstruktor 2Public CyclicBarrier (int Partys) {this (Partys, Null);}CyclicBarrier hat zwei Konstruktoren, bei denen Konstruktor 1 sein Kernkonstruktor ist. Hier können Sie die Anzahl der Teilnehmer in diesem Spiel (Anzahl der zu abgefangenen Threads) und die zum Ende dieses Spiels ausgeführten Aufgaben angeben. Sie können auch sehen, dass der Anfangswert der Zählerzahl auf Parteien eingestellt ist. Die Hauptfunktion der Cyclicbarrier -Klasse besteht darin, den Thread zu blockieren, der zuerst am Barrierepunkt ankommt und auf den nachfolgenden Thread wartet. Es bietet zwei Wartenmethoden, nämlich zeitgesteuerte Wartezeiten und nicht getimte Warten.
// Nicht-Timed-Wartezeit public int actait () löst InterruptedException aus, bastbarrierexception {try {return dowait (false, 0l); } catch (timeoutException toe) {Neuen Fehler werfen (toe); }} // Timed Waiting Public int Auseait (langfristige Zeit, Timeunit Unit) löst unterbrochene Ausnahme, brakbarrierexception, timeoutexception {return dowait (true, unit.tonanos (timeout));} ausEs ist ersichtlich, dass sie die Dowait-Methode nennen, ob es zeitgesteuert oder nicht abgestimmt ist, aber die eingegebenen Parameter sind unterschiedlich. Schauen wir uns an, was die Dowait -Methode tut.
// Kernwartungsmethode private int dowait (boolean zeitgesteuert, lange Nanos) löscht InterruptedException, brakbarrierexception, timeoutexception {Final Reentrantlock lock = this.lock; lock.lock (); Versuchen Sie {endgültige Generation G = Generation; // Überprüfen Sie, ob der aktuelle Zaun umgeschlagen wird, wenn (G. broken) {neue brakbarrierexception () werfen; } // Überprüfen Sie, ob der aktuelle Thread unterbrochen wird, wenn (thread.interrupted ()) {// Wenn der aktuelle Thread unterbrochen ist, werden die folgenden drei Dinge erledigt // 1. Blasen Sie den aktuellen Zaun nach unten // 2. Weck alle abgefangenen Threads // 3 auf. Wirf einen Interrupt -Ausnahme -Breakbarier (); neue InterruptedException () werfen; } // reduzieren Sie jedes Mal den Zählerwert durch 1 int index = -count; // Verringern Sie den Zählerwert auf 0, alle Threads müssen aufwachen und in die nächste Generation konvertieren, wenn (index == 0) {boolean ranAction = false; Versuchen Sie es mit {// die angegebene Aufgabe ausführen, bevor Sie alle Threads endgültig runnable command = barrierCommand aufwachen; if (command! = null) {command.run (); } ranAction = true; // Alle Themen wach und gehen Sie zur nächsten Generation NextGeneration (); Rückkehr 0; } endlich {// stellen Sie sicher, dass alle Threads erweckt werden können, wenn (! ranAction) {breakBarrier (); }}} // Wenn der Zähler nicht 0 ist, führen Sie diese Schleife für (;;) {try {// verteidigen Sie fest, um festzustellen, ob Sie zeitlich abgestimmt werden müssen, wenn (! Timed) {trip.await (); } else if (nanos> 0l) {nanos = trip.awaitnanos (nanos); }} catch (InterruptedException dh) {// Wenn der aktuelle Thread während des Wartens unterbrochen wird, schlag den Zaun nieder, um andere Threads aufzuwecken, wenn (g == generation &&! g.broken) {breakBarrier (); IE werfen; } else {// Wenn die Wartezeit auf dem Zaun abgeschlossen wurde, bevor die Interrupt -Ausnahme erwischt wird, wird die Interrupt -Operation als direkt thread bezeichnet. }} // Wenn der Thread wegen der umkippenden Operation geweckt wird, wird eine Ausnahme ausgelöst, wenn (g. broken) {neue brakbarrierexception () werfen; } // Wenn der Thread wegen des Ersatzvorgangs geweckt wird, gibt er den Wert des Zählers zurück, wenn (g! = Generation) {return index; } // Wenn der Thread wegen der Zeit geweckt wird, schlägt er den Zaun und wirft die Ausnahme aus, wenn (zeitlich && nanos <= 0l) {breakBarrier (); neue timeoutexception () werfen; }}} endlich {lock.unlock (); }}Die Kommentare im oben veröffentlichten Code sind ziemlich detailliert, daher wählen wir nur einige wichtige. Sie können sehen, dass die Anzahl jedes Mal in der Dowait -Methode um 1 reduziert wird. Nach dem Abbau wird sofort festgestellt, ob es gleich 0 entspricht. Wenn es gleich 0 ist, wird zunächst die zuvor angegebene Aufgabe ausgeführt. Nach der Ausführung wird die NextGeneration -Methode aufgerufen, um den Zaun an die nächste Generation zu übertragen. Bei dieser Methode werden alle Threads geweckt, der Zählerwert wird auf Parteien zurückgesetzt und die Zaunerzeugung wird zurückgesetzt. Nach der Ausführung der NextGeneration -Methode bedeutet dies, dass das Spiel in das nächste Spiel eintritt. Wenn der Zähler zu diesem Zeitpunkt nicht gleich 0 ist, wird die für die Schleife für die Schleife eingegeben. Entscheiden Sie, ob Sie trip.aawaitnanos (nanos) oder trip.await () -Methoden basierend auf den Parametern rufen. Diese beiden Methoden entsprechen dem Timing und dem nicht timierten Warten. Wenn der aktuelle Thread während des Wartens unterbrochen wird, wird die Breakbarier -Methode ausgeführt. Diese Methode wird als Breaking the Zaun bezeichnet, was bedeutet, dass das Spiel auf halber Strecke abgeschnitten wird, den gebrochenen Generationszustand auftrennt und alle Fäden aufweckt. Gleichzeitig bedeutet dies auch, dass während des Wartenprozesses ein Thread unterbrochen wird und das gesamte Spiel endet und alle zuvor blockierten Threads geweckt werden. Nachdem der Thread aufgewacht ist, werden die folgenden drei Urteile durchgeführt, um festzustellen, ob er aufgrund der Aufruf der Breakbarier -Methode aufgeweckt wird. Wenn ja, wird es eine Ausnahme machen; Sehen Sie, ob es durch einen normalen Ersatzvorgang geweckt wird. Wenn ja, wird der Wert des Zählers zurückgegeben; Sehen Sie, ob es wegen der Auszeit aufgeweckt wird. In diesem Fall wird Breakbarrier anrufen, um den Zaun zu brechen und eine Ausnahme zu machen. Hier sollte auch beachtet werden, dass das gesamte Spiel, wenn einer der Threads auf die Auszeit des Timeouts wartet, und andere Threads erweckt werden. Die folgenden veröffentlicht die spezifischen Codes für die NextGeneration -Methode und die Breakbarier -Methode.
// Zaun in die nächste Generation private void NextGeneration () {// alle Threads in der Bedingung Warteschlange Trip.Signalall () aufwecken; // Setzen Sie den Zählerwert auf die Anzahl der Threads, die abgefangen werden müssen. // Setzen Sie die Zaungenerierung zurück = new Generation ();} // Um den aktuellen Zaun privater void breakbarier () {// den aktuellen Zaunstatus festlegen, um die Erzeugung umzukehren.broken = true; // Setzen Sie den Zählerwert auf die Anzahl der Threads, die abgefangen werden müssen. // Alle Threads trip.Signalall ();} aufwachen;}Wir haben im Grunde die Prinzipien des Cyclicbarriers durch den oben genannten Quellcode erklärt. Erfahren wir mehr über seine Verwendung anhand eines Pferderennens.
Class Horse Implements Runnable {private static int counter = 0; private endgültige int id = counter ++; private int strides = 0; private statische zufällige Rand = neuer zufälliger (47); private statische Cyclicbarrier -Barriere; Public Horse (CyclicBarrier b) {Barriere = b; } @Override public void run () {try {while (! Thread.interrupted ()) {synchronized (this) {// Die Pferderennen führen jedes Mal, wenn sie Strides += Rand.Nextint (3) ausführen, zufällig mehrere Schritte aus; } barriere.aawait (); }} catch (Ausnahme e) {e.printstacktrace (); }} public String tRACKS () {StringBuilder s = new StringBuilder (); für (int i = 0; i <getstrides (); i ++) {S.Append ("*"); } S.Append (id); return s.tostring (); } public synchronisierte int getstrides () {return strides; } public String toString () {return "Pferd" + id + ""; }} public Class Horserace implementiert runnable {private statische endgültige int finde_line = 75; private statische Liste <Horses> pferses = new ArrayList <Horse> (); private static ExecutorService exec = ausführende.newcachedThreadpool (); @Override public void run () {stringBuilder s = new StringBuilder (); // Spurgrenze für (int i = 0; i <figming_line; i ++) {S.Append ("="); } System.out.println (s); // Print Horse Racing Track für (Pferd: Pferde) {System.out.println (Horse.Tracks ()); } // Besprechen Sie, ob Sie für (Pferd: Pferde) {if (Horse.getstrides ()> = Finish_line) {System.out.println (Horse + "Won!"); exec.shutdownnow (); zurückkehren; }} // Die angegebene Zeit in die nächste Runde ausruhen {TimeUnit.Milliseconds.sleep (200); } catch (InterruptedException e) {System.out.println ("Barrier-Action-Schlaf unterbrochen"); }} public static void main (String [] args) {cyclicbarrier barrier = neuer cyclicbarrier (7, neuer posterace ()); für (int i = 0; i <7; i ++) {Horse = New Horse (Barriere); Pferde.Add (Pferd); exec.execute (Pferd); }}}}In diesem Pferderennrennprogramm wird hauptsächlich die aktuellen Tracks jedes Pferderennens auf der Konsole verwendet, um einen dynamischen Display -Effekt zu erzielen. Es gibt mehrere Runden des gesamten Rennens. Jedes Pferderennen unternimmt zufällig ein paar Schritte und ruft dann die Warteverfahren zum Warten auf. Wenn alle Pferde eine Runde abschließen, wird die Aufgabe ausgeführt, um die aktuelle Spur aller Pferderennen zur Konsole zu drucken. Auf diese Weise wächst die Flugbahn jedes Pferderennens in jeder Runde weiter. Wenn die Flugbahn eines der Pferderennen zum ersten Mal auf den angegebenen Wert zunimmt, endet das gesamte Rennen und das Pferderennen wird der Gewinner des gesamten Rennens! Die laufenden Ergebnisse des Programms sind wie folgt:
Zu diesem Zeitpunkt werden wir Cyclicbarrier unweigerlich mit Countdownlatch vergleichen. Beide Klassen können eine Reihe von Threads implementieren, die warten, bevor sie eine bestimmte Bedingung erreichen. Sie haben einen Zähler im Inneren. Wenn der Zählerwert kontinuierlich auf 0 reduziert wird, werden alle blockierenden Fäden geweckt. Der Unterschied besteht darin, dass der Zähler von Cyclicbarrier selbst gesteuert wird, während der Zähler von Countdownlatch vom Benutzer gesteuert wird. In CyclicBarrier ruft ein Thread das Warte -Methode nicht nur auf, sondern reduziert den Zähler auch um 1. In Countdownlatch ruft ein Thread das Warte -Methode nur auf, ohne den Wert des Zählers zu verringern. Darüber hinaus kann Countdownlatch nur eine Runde abfangen, während Cyclicbarrier zirkuläres Interception implementieren kann. Im Allgemeinen kann die Verwendung von CyclicBarrier die Funktion von Countdownlatch implementieren, aber ansonsten kann dies nicht. Beispielsweise kann das obige Pferderennrennprogramm nur mit Cyclicbarrier implementiert werden. Kurz gesagt, die Ähnlichkeiten und Unterschiede zwischen diesen beiden Kategorien sind ungefähr gleich. Wenn Sie Cyclicbarrier und Countdownlatch verwenden, müssen die Leser es immer noch selbst erfassen.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.