CountdownLatch -Quellcodeanalyse - Countdown ()
Der vorherige Artikel wurde über das Prinzip von Await () in Countdownlatch von der Quellcodeebene gesprochen. In diesem Artikel geht es um Countdown ().
public void Countdown () {// Countdownlatch Sync.Releaseshared (1);} ↓ öffentliche endgültige boolesche Freigabe (int arg) {// aqs if (tryReleaseshared (arg)) {doreleaseshared (); zurückkehren; } return false;} ↓ Protected boolean tryreleaseshared (int releases) {//countdownlatch.sync // Decrement -Anzahl; Signal beim Übergang zu Null für (;;) {int c = getState (); if (c == 0) return false; int nextc = c-1; if (vergleicheSetState (c, nextc)) return nextc == 0; }}Über den Konstruktor Countdownlatch End = new Countdownlatch (2); Der Zustand ist auf 2 eingestellt, also c == 2, nextc = 2-1,
Stellen Sie dann den Zustand auf 1 durch die folgende CAS -Operation ein.
geschützte endgültige boolesche VergleicheSetSetState (int erwartet, int update) {// siehe unten für Intrinsics Setup, um diese Rückgabe zu unterstützen. }Zu diesem Zeitpunkt ist NextC nicht 0 und gibt falsch zurück. Warten Sie, bis die Methode Countdown () zweimal aufgerufen wird, Status == 0, NextC == 0, und gibt zu diesem Zeitpunkt True zurück.
Geben Sie die Methode Doreleaseshared () ein.
doreleaseshared (); ↓ private void doreleaseshared () { / * * Stellen Sie sicher, dass sich eine Freisetzung ausbreitet, auch wenn andere * In-Progress-Erwerben /Freisetzungen vorhanden sind. Dies erfolgt in der üblichen * Art, zu versuchen, den Kopf zu planen, wenn es ein Signal benötigt. Wenn dies jedoch nicht der Fall ist, wird der Status so eingestellt, dass sich die Ausbreitung fortsetzt, um sicherzustellen, dass bei der Freilassung die Ausbreitung fortgesetzt wird. * Zusätzlich müssen wir schauen, falls ein neuer Knoten hinzugefügt wird *, während wir dies tun. Im Gegensatz zu anderen Verwendungszwecken von * UnparksSuScessor müssen wir wissen, ob CAS, um den Status zurückzusetzen *, fehlschlägt, wenn ja, wenn ja, das Wiederaufnehmen. */ für (;;) {Knoten H = Kopf; if (h! = null && h! = schwanz) {int ws = h.waitstatus; if (ws == node.Signal) {if (! vergleicheSetwaitStatus (h, node.signal, 0)) Fortsetzung; // Schleife zur Wiederholung von Fällen UnparksScessor (H); } else if (ws == 0 &&! vergleicheSetwaitStatus (h, 0, node.propagate)) Fortsetzung; // Schleife auf fehlgeschlagener CAS} if (H == Kopf) // Schleife Wenn der Kopf geändert wurde; }}Erinnern Sie sich das wartende Warteschlangenmodell zu diesem Zeitpunkt.
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Zu diesem Zeitpunkt ist der Kopf nicht null oder schwanz. WaitStatus == Node.Signal, Geben Sie also das Urteil ein, wenn (! vergleicheSetwaitStatus (h, node.signal, 0)).
if (! vergleicheSetwaitStatus (h, knoten.Signal, 0)) ↓ /*** cas WaitStatus -Feld eines Knotens. */Private statische endgültige booleale VergleicheSetwaitStatus (Knotenknoten, int erwartet, int update) {return unafe.comPareAndswapint (Knoten, WaitStatusOffset, erwarten, update);}Diese CAS -Operation setzt den Zustand auf 0, was bedeutet, dass Kellner im Kopf zu diesem Zeitpunkt 0 ist. Das Warteschlangenmodell lautet wie folgt
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Diese Methode gibt wahr zurück. Geben Sie die UnparksScessor (H) ein;
PEPARKSUCCESSOR (H); ↓ private void UnparksuSccessor (Knotenknoten) { / * * Wenn der Status negativ ist (dh mögliche Signalbedarf) Versuchen Sie, in Erwartung der Signalübertragung zu löschen. Es ist in Ordnung, wenn dies * fehlschlägt oder wenn der Status durch Warten von Thread geändert wird. */ int ws = node.waitstatus; if (ws <0) vergleicheSetwaitStatus (Knoten, WS, 0); / * * Thread zu Unpark wird im Nachfolger gehalten, was normalerweise nur der nächste Knoten ist. Aber wenn es abgesagt oder anscheinend null ist, * durchqueren Sie nach hinten vom Schwanz, um den tatsächlichen * nicht besorgten Nachfolger zu finden. */ Node s = node.next; if (s == null || swaitstatus> 0) {s = null; für (Knoten T = Tail; t! = null && t! = node; t = t.prev) if (t.waitstatus <= 0) s = t; } if (s! = null) locksSupport.unpark (S. thread);}S ist der Nachfolgerknoten des Kopfes, dh der Knoten mit dem aktuellen Thread. s! = null und s.waitstatus == 0, also betreten Sie locksupport.unpark (s.Thread);
public static void Unpark (Thread Thread) {if (thread! = null) unafe.unpark (thread); }Das heißt, der Faden, der blockiert wird. Der Schiedsrichter durfte die Pfeife blasen!
Das Prinzip von Countdown () ist sehr klar.
Jedes Mal, wenn die Countdown () -Methode ausgeführt wird, wird der Status um 1. bis zum Zustand == 0 reduziert, die in der Warteschlange blockierten Threads werden freigegeben, und die Threads in nachfolgenden Knoten werden gemäß dem Zustand des Kellnerstatus im Vorgängerknoten freigegeben.
OK, kehren Sie zur Frage des vorherigen Artikels zurück, wann wird die folgende Schleife ausbricht (die Schleife in der wartenden Methode)
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 geben State == 0 die SetheadandAndPropagate -Methode ein.
SetheadandPropagate (Knoten, R); ↓ private void SetheadandPropagate (Knotenknoten, int -Propagate) {Knoten H = Kopf; // Old Head zum Scheck unter Sethead (Knoten) aufzeichnen; / * * Versuchen Sie, den nächsten Knoten der nächsten in der Warteschlange zu signalisieren, wenn: * Ausbreitung durch Anrufer angezeigt wurde, oder wurde von einer früheren Operation (Hinweis: Dies verwendet, als H. waitstatus entweder vor * oder nach Sethead) (Hinweis: Dies verwendet Signal-Check für Waitstatus, weil * Propagatstatus zu einem Signal. * Unnötiges Aufwachen, aber nur, wenn mehrere * Rennspuren erwerben/veröffentlichen. Die meisten brauchen jetzt oder bald Signale * sowieso *. */ if (Propagate> 0 || H == NULL || H.Waitstatus <0 || (h = head) == null || H.Waitstatus <0) {node s = node.next; if (s == null || S. isshared ()) doreleaseshared (); }} ↓ private void Sethead (Knotenknoten) {head = node; node.thread = null; node.prev = null;}Diese Methode ändert den Nachfolgerknoten des Kopfes in den Kopf. Nach dieser Methode wird der nächste Knoten des Knotens auf Null gesetzt, und das Modell wird zur folgenden Abbildung
vorläufig +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Das heißt, Knotenkopfschwanz und andere Dinge werden auf Null gesetzt und warten darauf, dass GC recycelt. Zu diesem Zeitpunkt kehren Sie zurück, springen Sie aus der für die Schleife heraus, und die Warteschlange wird gelöscht.
Hier ist eine Demonstration des gesamten Prozesses
SetheadandPropagate (Knoten, R); +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Thread = NULL | <---- Node (Schwanz) | CurrentThread | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Knoten (Schwanz) | CurrentThread | + ---------------------------+ ↓ +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Der Kern von Countdownlatch ist eine blockierende Thread -Warteschlange, eine Warteschlange, die aus einer verknüpften Liste erstellt wurde, die Thread und Waitstatus enthält, in der WaitStatus den Status des Nachfolge -Knoten -Threads beschreibt.
Staat ist eine sehr wichtige Flagge. Beim Bau wird es auf den entsprechenden N -Wert eingestellt. Wenn n! = 0, wird die blockierende Warteschlange ständig blockiert, es sei denn, der Faden wird unterbrochen.
Jedes Mal, wenn die Countdown () -Methode aufgerufen wird, wird Status-1 verwendet, und die Await () -Methode wird verwendet, um den Thread hinzuzufügen, der die Methode zur Blockierungswarteschlange bis zum Status == 0 aufruft, und der Thread kann nicht freigegeben werden.
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.