Countdownlatch -Quellcode -Analyse - Await (), der spezifische Inhalt ist wie folgt
Im vorherigen Artikel wurde darüber gesprochen, wie Countdownlatch verwendet werden soll. In diesem Artikel wird über das Prinzip von Await () aus der Quellcodeebene sprechen.
Wir wissen bereits, dass das Warten den aktuellen Thread in einem Blockierungszustand halten kann, bis die Anzahl der Latch -Anzahl (oder Fadenunterbrechung) ist.
Unten ist der Quellcode.
end.aait (); ↓ public void actait () löscht InterruptedException {sync.acquiresharedinterriptable (1);} ausSynchronisation ist die innere Klasse von Countdownlatch. Hier ist seine Definition.
Private statische endgültige Klasse Sync erweitert Abstractqueedsynchronizer {...}Es erbt den AbstractQueuedsynchronizer. Abstractqueuedsynchronizer Diese Klasse gehört zu einer sehr wichtigen Klasse in Java -Threads.
Es bietet ein Framework, um Blockierschlösser und verwandte Synchronisatoren (wie Signale, Ereignisse usw.) zu implementieren, die auf FIFO -Warteschlangen angewiesen sind.
Machen Sie weiter und springen Sie in die Abstractqueuedsynchronizer -Klasse.
sync.acquiresharedinterruptisle (1); ↓ öffentliche endgültige void erwerben, die interruptedException {if (thread.interrupted ()) neue InterruptedException () veröffentlichen; if (tryacquireshared (arg) <0) doacquiresharedinterrupticle (arg);}Hier gibt es zwei Urteile. Stellen Sie zunächst fest, ob der Thread unterbrochen wird, und fällen Sie dann das nächste Urteil. Hier schauen wir uns hauptsächlich das zweite Urteil an.
Protected Int tryAcquireshared (int erwirbt) {return (getState () == 0)? 1: -1;}Es ist zu beachten, dass die Methode tryAcquiresharedary synchronisiert ist.
Obwohl es in Abstractqueuedsynchronizer implementiert wird, besteht die Standardimplementierung darin, eine Ausnahme zu machen.
Tryacquireshared Diese Methode wird verwendet, um abzufragen, ob der Status des aktuellen Objekts das Schloss erwerben kann.
Wir können sehen, dass wir synchron den entsprechenden Int -Wert zurückgeben, indem wir feststellen, ob der Zustand 0 ist.
Was bedeutet Staat?
/*** Der Synchronisationszustand. */ privates volatiles Int -Zustand;
Der obige Code zeigt deutlich, dass der Zustand den Synchronisationsstatus darstellt.
Es ist zu beachten, dass der Zustand das flüchtige Schlüsselwort verwendet, um es zu ändern.
Das volatile Schlüsselwort kann sicherstellen, dass die Änderung des Zustands sofort auf den Hauptspeicher aktualisiert wird. Wenn andere Threads lesen müssen, wird der neue Wert im Speicher gelesen.
Das heißt, die Sichtbarkeit des Staates ist garantiert. Es sind die neuesten Daten.
Was ist der Staat, der hierher kommt?
Hier müssen wir uns den Konstruktor von Countdownlatch ansehen.
Countdownlatch End = new Countdownlatch (2); ↓ public Countdownlatch (int count) {if (count <0) neue IllegalArgumentException ("count <0"); this.ync = new sync (count);} ↓ sync (int count) {setState (count);}Es stellt sich heraus, dass die Zahlen im Konstruktor verwendet werden, um den Zustand festzulegen.
Also haben wir hier state == 2. Tryacquireshared -Returns -1. Unten eingeben
doacquiresharedinterrupticle (arg); ↓ private void doacquiresharedinterrupticled (int arg) löscht InterruptedException {final node node = addwaiter (node.shared) aus; boolean fehlgeschlagen = wahr; try {for (;;) {endgültiger Knoten p = node.prred -Antänger (); if (p == Kopf) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandSpropagate (Knoten, r); P.Next = null; // hilf GC fehlgeschlagen = false; zurückkehren; }} if (sildParkAfterFailedacquire (p, Knoten) && ParkandCheckinterrupt ()) Wirf eine neue InterruptedException (); }} schließlich {if (fehlgeschlagen) CancelAcquire (Knoten); }}OK, dieser Code ist etwas lang und einige Funktionen werden darin aufgerufen. Schauen wir es uns nacheinander an.
Ein neuer Klassenknoten erscheint in der ersten Zeile.
Der Knoten ist eine interne Klasse in AQs (Abstractqueedsynchronizer) Klasse, die eine Kettenstruktur definiert. Wie unten gezeigt.
+------+PREV+------++-----+Kopf | | <---- | | <---- | | | Schwanz +------ + +------ + +----- +
Denken Sie an diese Struktur.
Es gibt auch eine Methode in der ersten Zeile von Code -Addwaiter (Node.shared).
Addwaiter (node.shared) //node.shared bedeutet, dass sich der Knoten im gemeinsam genutzten Modus befindet ↓ private Knoten Addwaiter (Knotenmodus) {Node node = neuer Knoten (Thread.CurrentThread (), Modus); // Versuchen Sie den schnellen Pfad von ENQ; Backup auf volle ENQ auf Fehlerknoten Pred = Tail; // privates transientes flüchtiger Knotenschwanz; if (pred! = null) {node.prev = pred; if (vergleicheStettail (Pred, Knoten)) {Pred.Next = Node; Return Node; }} enq (Knoten); Return Node;}Zunächst wird ein Knoten konstruiert und der aktuelle Faden gespeichert. Der Modus ist ein gemeinsamer Modus.
Schwanz bedeutet, dass das Warteschlangenende der wartenden Warteschlange in diesem Moment null ist. So pred == null tritt enq (node) ein;
ENQ (Knoten) ↓ private Knoten enq (endgültiger Knotenknoten) {für (;;) {Knoten t = schwanz; if (t == null) {// muss initialisieren, wenn (vergleicheDead (neuer node ())) schwanz = Kopf; } else {node.prev = t; if (vergleicheStettail (t, Knoten)) {t.Next = node; return t; }}}}Der gleiche Schwanz ist null, eingeben VergleicheDeadead.
VergleicheDeadead (neuer Node ()) ↓/*** CAS -Kopffeld. Verwendet nur von ENQ. */Private Final Boolean VergleicheAnDeadead (Knoten -Update) {return unafe.comPareAndswapObject (this, HeadOffset, NULL, UPDATE);}Dies ist eine CAS -Operation. Wenn der Kopf null ist, wird der Kopf der wartenden Warteschlange auf den Aktualisierungswert eingestellt, der ein neuer Knoten ist.
Schwanz = Kopf; Dann ist der Schwanz zu diesem Zeitpunkt nicht mehr null. Geben Sie den nächsten Zyklus ein.
Diesmal zeigen Sie zuerst den vorherrschenden Zeiger des Knotens auf den Schwanz, stellen Sie den Knoten durch eine CAS -Operation auf den Schwanz ein und geben Sie den Schwanz der Warteschlange zurück, dh Knoten.
Das Modell der wartenden Warteschlange ändert sich wie folgt wie folgt
+ ------+ pre +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OK, wenn Sie hier erhalten, ist die Warte -Methode zurückgegeben, es ist ein Thread, der dem Knoten des aktuellen Threads entspricht.
Kehren Sie zu Doacquiresharedinterrupticled (int arg) zurück und geben Sie die folgende Schleife ein.
für (;;) {endgültiger Knoten p = node.prred -Antimer (); if (p == Kopf) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandSpropagate (Knoten, r); P.Next = null; // hilf GC fehlgeschlagen = false; zurückkehren; }} if (sildParkAfterFailedacquire (p, Knoten) && ParkandCheckinterrupt ()) Wirf eine neue InterruptedException ();}Zu diesem Zeitpunkt ist unter der Annahme, dass der Staat immer noch größer als 0 ist, und dann zu diesem Zeitpunkt r <0, also geben Sie die sildParkAfterFailedacquire -Methode ein.
SHATSPARKAFFTERFAILEDACQUIRE (P, Knoten) ↓ privat statische boolean sildParkASTTERFAILEDACQUIRE (Knotenpred, Knotenknoten) {int ws = pred.waitstatus; if (ws == node.Signal) // statisches endgültiges int signal = -1; / * * Dieser Knoten hat den Status bereits festgelegt, um eine Version * zu befragen *, um ihn zu signalisieren, damit er sicher parken kann. */ return true; if (ws> 0) { / * * Vorgänger wurde abgesagt. Überspringen Sie Vorgänger und * angezeigt erneut. */ do {node.prev = pred = pred.prev; } while (pred.waitstatus> 0); Pred.Next = Knoten; } else { / * * WaitStatus muss 0 oder propagieren. Geben Sie an, dass wir * ein Signal brauchen, aber noch nicht parken. Der Anrufer muss * wiederholen, um sicherzustellen, dass er vor dem Parken nicht erwerben kann. */ vergleicheSetwaitStatus (Pred, WS, Node.Signal); } return false;} ↓/*** Cas WaitStatus -Feld eines Knotens. */Private statische endgültige booleale VergleicheSetwaitStatus (Knotenknoten, int erwartet, int update) {return unafe.comPareAndswapint (Knoten, WaitStatusOffset, erwarten, update);}Sie können sehen, dass das Sollte -Afterfailedacquire auch bis zum Vergleich von Vergleichewaitstatus übernommen wird.
VergleicheSetwaitStatus setzen den WaitStatus of Prev in Node.signal.
Node.Signal bedeutet, dass die Threads in nachfolgenden Knoten sichtbar machen müssen (ähnlich wie geweckt). Diese Methode gibt falsch zurück.
Nach diesem Zyklus wird das Warteschlangenmodell zum folgenden Zustand
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
Da SHOTSPARKAFTTERFAILEDACQUIRE RECISS FALSE RETSIONS RETTIERT, werden wir uns die folgenden Bedingungen nicht mehr ansehen. Setzen Sie die Schleife für (;;) fort.
Wenn der Staat immer noch größer als 0 ist, geben Sie erneut in shentparkAfterfailedacquire ein.
Diesmal, da WaitStatus im Kopf ein Knoten ist.
Diesmal muss ich die ParkandCheckinterrupt -Methode sehen.
private final boolean ParkandCheckinterrupt () {locksupport.park (this); return thread.interrupted (); }OK, der Thread wird nicht unterbrochen, also kehren Sie falsch zurück. Setzen Sie die Schleife für (;;) fort.
Wenn der Zustand immer größer als 0 ist und der Thread nicht unterbrochen wird, ist er immer in dieser Schleife. Das heißt, die in dem vorherigen Artikel erwähnten Schiedsrichter, dass sie immer zögerten, das Ende des Spiels bekannt zu geben.
Unter welchen Umständen wird die Schleife also ausbrechen? Das heißt, unter welchen Umständen wird angeben weniger als 0? Ich werde den nächsten Artikel erklären.
Zusammenfassend lässt sich sagen, dass die Wartezeit () eine Warteschlange in die Initialisierung einer Warteschlange (Status> 0) in eine Warteschlange hinzufügen und den Thread -Status des Nachfolgerknotens mit WaitStatus gewartet werden muss.
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.