In dem vorherigen Artikel "Java Concurrency-Serie [1] ----- AbstractQueuedsynchronizer-Quellcode-Analyse" haben wir einige grundlegende Konzepte von AbstractQueusynchronizer eingeführt, wobei hauptsächlich darüber gesprochen wurde, wie der Warteschlangenbereich von AQs implementiert ist, was der ausschließliche Modus und der freigegebene Modus und der Verständnis des Wartezustands von Nodes ist. Das Verständnis und Beherrschen dieser Inhalte ist der Schlüssel zum nachfolgenden Lesen des AQS -Quellcode. In diesem Artikel werden wir vorstellen, wie Knoten die Synchronisation -Warteschlange im exklusiven Modus eingeben und welche Operationen vor dem Verlassen der Synchronisationswarteschlange ausgeführt werden. AQS bietet drei Möglichkeiten, um Sperren im exklusiven Modus und im gemeinsamen Modus zu erhalten: Nicht reagierender Thread-Interrupt-Akquisition, Response-Thread-Interrupt-Akquisition und Zeitüberschreitungsaufnahme. Die Gesamtschritte dieser drei Methoden sind ungefähr gleich mit nur wenigen verschiedenen Teilen. Wenn Sie also eine Methode verstehen und die Implementierung anderer Methoden betrachten, sind Sie ähnlich. In diesem Artikel werde ich mich auf die Erfassungsmethode konzentrieren, um nicht auf Thread -Interrupts zu reagieren, und die anderen beiden Methoden werden auch über die Inkonsistenzen sprechen.
1. Wie erhalten Sie Schlösser mit nicht reagierenden Faden-Interrupts?
// nicht auf Interrupt -Methodenakquisition reagieren (exklusiver Modus) öffentliche endgültige Leere erwerben (int arg) {if (! Tryacquire (arg) && accoirequeued (Addwaiter (node.exclusive), arg)) {selfterrupt (); }}Obwohl der obige Code einfach aussieht, führt er die 4 in der folgenden Abbildung gezeigten Schritte in der Reihenfolge aus. Im Folgenden werden wir Schritt für Schritt demonstrieren und analysieren.
Schritt 1 :! tryacquire (arg)
// Versuchen Sie, den geschützten booleschen lock (exklusiven Modus) zu erwerben (int arg) {Neue nicht unterstützte OperationSexception ();};}Zu dieser Zeit kam jemand und er versuchte zuerst an die Tür zu klopfen. Wenn er feststellte, dass die Tür nicht verschlossen war (tryacquire (arg) = wahr), würde er direkt eingehen. Wenn Sie feststellen, dass die Tür verschlossen ist (tryacquire (arg) = false), dann führen Sie den nächsten Schritt aus. Diese Tryacquire -Methode bestimmt, wann das Schloss geöffnet ist und wann das Schloss geschlossen ist. Diese Methode muss durch Unterklassen überschrieben und die Beurteilungslogik im Inneren umschreiben.
Schritt 2: Addwaiter (Node.Exclusive)
// Wickeln Sie den aktuellen Thread in einen Knoten und fügen Sie ihn zum Schwanz der Synchronisation -Warteschlange hinzu. // Erhalten Sie die Referenz des Heckknotens der Synchronisations -Warteschlange -Knoten -Präd = Schwanz; // Wenn der Heckknoten nicht leer ist, bedeutet dies, dass die Synchronisationswarteschlange bereits einen Knoten hat, wenn (pred! = Null) {// 1. Zeigen Sie auf den Strom -Heckknotenknoten. Prev = Pred; // 2. Stellen Sie den aktuellen Knoten auf den Heckknoten fest, wenn (vergleicheSettail (Pred, Knoten)) {// 3. Zeigen Sie den Nachfolger des alten Schwanzknotens auf den neuen Heckknoten Pred.Next = Knoten; Return Node; }} // ansonsten bedeutet dies, dass die Synchronisationswarteschlange nicht initialisiert wurde, enq (Knoten); Rückgabeknoten;} // Knoten Enqueue private Knoten enq (endgültiger Knotenknoten) {für (;;) {// Die Referenz des Heckknotens des Synchronisations -Queue -Knotens T = Heck erhalten; // Wenn der Heckknoten leer ist, bedeutet dies, dass die Synchronisationswarteschlange nicht initialisiert wurde, wenn (t == null) {// die Synchronisationswarteschlange initialisieren if (vergleicheDead (neuer node ()) {Tail = Head; }} else {// 1. Zeigen Sie auf den aktuellen Heckknotenknoten.Prev = t; // 2. Stellen Sie den aktuellen Knoten auf den Heckknoten fest, wenn (vergleicheSettail (t, Knoten)) {// 3. Zeigen Sie den Nachfolger des alten Schwanzknotens auf den neuen Heckknoten t.Next = Knoten; return t; }}}}Die Ausführung in diesen Schritt zeigt an, dass beim ersten Mal fehlgeschlagen ist, sodass die Person eine Zahlenkarte für sich selbst erhält und den Warteschlangenbereich in die Warteschlange eingibt. Wenn er die Nummernkarte erhält, wird er erklären, wie er den Raum besetzen möchte (exklusiver Modus oder Freigabemodus). Beachten Sie, dass er sich zu diesem Zeitpunkt nicht hinsetzte und sich ausruhte (sich aufhängen).
Schritt 3: Erwerb (Addwaiter (Node.Exclusive), Arg)
// das Sperre auf ununterbrochene Weise erfassen (exklusiver Modus) endgültig boolean erfasste (endgültiger Knotenknoten, int arg) {boolean failed = true; Versuchen Sie {boolean unterbrochen = false; für (;;) {// die Referenz des vorherigen Knotens des angegebenen Knotens endgültiger Knoten p = node.prred -Angänger (); // Wenn der aktuelle Knoten der erste Knoten der Synchronisationswarteschlange ist, versuchen Sie, das Sperre zu erwerben, wenn (p == Kopf && tryacquire (arg)) {// den angegebenen Knoten als Headknoten Sethead (Knoten) festlegen; // Um die Müllsammlung zu unterstützen, klären Sie den Nachfolger des vorherigen Kopfknotens P.Next = null; // Setzen Sie den erfolgreichen Erwerbszustand fehlgeschlagen = false; // Rückgabe des unterbrochenen Zustands wird die gesamte Schleife hier ausgeführt, die Ausgangsrendite unterbrochen; } // Ansonsten bedeutet dies, dass der Sperrstatus noch nicht verfügbar ist. Bestimmen Sie zu diesem Zeitpunkt, ob der aktuelle Thread suspendiert werden kann // Wenn das Ergebnis wahr ist, kann der aktuelle Thread ausgesetzt werden. Andernfalls wird die Schleife in diesem Zeitraum fortgesetzt. Der Thread reagiert nicht auf den Interrupt, wenn (shaftenparkaFterfailEdacquire (p, Knoten) && ParkandCheckinterrupt () {Interrupted = true; }}} schließlich {// stellen Sie sicher, dass Sie die Erfassung absagen, wenn (fehlgeschlagen) {CancelAcquire (Knoten); }}} // Beurteilen Sie, ob der aktuelle Knoten private statische boolean sotterFterFailedacquire (Knotenpred, Knotenknoten) {// den Wartezustand des Vorwärtsknotens int WS = Pred.waitstatus erhalten; // Wenn der Vorwärtsknotenstatus ein Signal ist, bedeutet dies, dass der Vorwärtsknoten den aktuellen Knoten aufweckt, sodass der aktuelle Knoten sicher suspendiert wird, wenn (WS == node.signal) {return true; } if (ws> 0) {// Die folgende Operation soll alle abgebrochenen Vorwärtsknoten in der Synchronisationswarteschlange do {node.prev = pred = pred.prev; } while (pred.waitstatus> 0); Pred.Next = Knoten; } else {// Zu diesem Zweck bedeutet dies, dass der Status des Vorwärtsknotens kein Signal ist, und es ist wahrscheinlich gleich 0. Auf diese Weise weckt der Vorwärtsknoten den aktuellen Knoten nicht auf. } return false;} // den aktuellen Thread Private Final Boolean ParkandCheckinterrupt () {locksupport.park (this); return thread.interrupted ();}Nachdem er das Zahlenzeichen erhalten hat, wird er diese Methode sofort implementieren. Wenn ein Knoten zum ersten Mal in den Warteschlangenbereich eintritt, gibt es zwei Situationen. Einer ist, dass er feststellt, dass die Person vor ihm seinen Platz verlassen und den Raum betreten hat, damit er sich nicht hinsetzt und sich ausruht und erneut an die Tür klopft, um zu sehen, ob das Kind fertig ist. Wenn die Person im Inneren gerade fertig war, würde er sich einsetzen, ohne sich selbst anzurufen. Andernfalls müsste er überlegen, sich eine Weile hinzusetzen und sich auszuruhen, aber er war immer noch besorgt. Was wäre, wenn ihn niemand erinnert, nachdem er sich hinsetzt und eingeschlafen war? Er hinterließ eine kleine Notiz auf dem Sitz des Mannes vorne, damit die Person, die innen herauskam, ihn nach dem Sehen der Notiz wecken konnte. Eine andere Situation war, dass als er in die Warteschlange kam und feststellte, dass mehrere Personen vor sich standen, er sich für eine Weile hinsetzen konnte, aber davor würde er immer noch eine Notiz auf dem Sitz der Person vor dem Vordergrund hinterlassen (er schlief bereits zu diesem Zeitpunkt), damit die Person ihn vor dem Ausscheiden aufwecken konnte. Wenn alles fertig ist, schläft er friedlich. Beachten Sie, dass wir sehen, dass das gesamte für die Schleife nur einen Ausgang hat, dh sie kann erst nach dem erfolgreichen Erwerb des Threads ausgehen. Bevor das Schloss erhalten wird, wird es immer in der ParkandCheckinterrupt () -Methode der for -Schleife aufgehängt. Nachdem der Thread erweckt wurde, führt er auch weiterhin die für Schleife von diesem Ort aus.
Schritt 4: selfterrupt ()
// Der aktuelle Thread unterbricht sich private static void selfterrupt () {thread.currentThread (). Interrupt (); }Da der gesamte Thread oben in der Methode ParkandCheckinterrupt () der für die Loop aufgehängt wurde, reagiert er auf keine Form von Thread -Interrupt, bevor er erfolgreich erworben wird. Erst wenn der Thread das Schloss erfolgreich erwirbt und aus der für die für die Schleife kommt, prüft er, ob jemand in diesem Zeitraum, den Thread zu unterbrechen, auffordert. Wenn ja, rufen Sie die selfterrupt () -Methode an, um sich selbst zu hängen.
2. Wie erhalten Sie Schlösser als Reaktion auf Thread -Interrupts?
// Erwerben des Sperrens im unterbruptiblen Modus (exklusiver Modus) private void doacquireInterrupticled (int arg) löscht die InterruptedException aus {// den aktuellen Thread in einen Knoten einwickeln und in die Synchronisation -Warteschlange endgültig Node Node = Addwaiter (Node.Exclusive); boolean fehlgeschlagen = wahr; try {for (;;) {// den vorherigen Knoten endgültig Node p = node.Pred -Degreor () abrufen; // Wenn P ein Kopfknoten ist, versucht der aktuelle Thread, das Schloss erneut zu erwerben, wenn (p == Kopf && tryacquire (arg)) {Sethead (Knoten); P.Next = null; // hilf GC fehlgeschlagen = false; // Rückkehr zurück, nachdem das Schloss erfolgreich erworben wurde; } // Wenn der Zustand erfüllt ist, wird der aktuelle Thread suspendiert. Zu diesem Zeitpunkt wird ein Interrupt beantwortet und eine Ausnahme ausgelöst, wenn (shollparkAfterfailedacquire (P, Knoten) && ParkandCheckinterrupt ()) {// Wenn der Thread erweckt wird, wird eine Ausnahme geworfen, wenn die Interrupt -Anfrage gefunden wird. neue InterruptedException () werfen; }}} schließlich {if (fehlgeschlagen) {covornacquire (node); }}}Die Response-Thread-Interrupt-Methode und die nicht responsive Faden-Interrupt-Methode sind in ungefähr dem Prozess des Erhaltens von Schlösser gleich. Der einzige Unterschied besteht darin, dass nach dem Aufwachen des Threads aus der ParkandCheckinterrupt -Methode überprüft wird, ob der Thread unterbrochen wird. Wenn ja, wird eine InterruptedException -Ausnahme ausgelöst. Anstatt auf Thread Interrupt -Erwerbssperrung zu reagieren, wird der Interrupt -Status nach Erhalt der Interrupt -Anforderung erst festgelegt und beendet die aktuelle Methode zum Erwerb des Schlosses nicht sofort. Es wird nicht entscheiden, ob sie sich auf der Grundlage des Interrupt -Zustands hängen sollen, nachdem der Knoten das Schloss erfolgreich erworben hat.
3. Wie kann ich die Zeitüberschreitung festlegen, um das Schloss zu erwerben?
// Das Schloss mit einem begrenzten Zeitlimit (exklusiven Modus) privat boolean doacquirenanos (int arg, long Nanostimeout) unterbrochener InterruptedException {// Die aktuelle Systemzeit lange letztes letztes letztes abzusetzen = system.nanotime (); // Das aktuelle Thread in einen Knoten einwickeln und in die Synchronisation Queue Final Node Node = Addwaiter (node.exclusive) hinzufügen; boolean fehlgeschlagen = wahr; try {for (;;) {// den vorherigen Knoten endgültig Node p = node.Pred -Degreor () abrufen; // Wenn der vorherige Knoten ein Kopfknoten ist, versucht der aktuelle Thread, das Sperre erneut zu erwerben, wenn (p == Kopf && tryacquire (arg)) {// den Kopfknoten Sethead (Knoten) aktualisieren; P.Next = null; fehlgeschlagen = falsch; zurückkehren; } // Sobald die Zeitüberschreitungszeit aufgebraucht ist, beenden Sie die Schleife direkt, wenn (Nanostimeout <= 0) {return false; } // Wenn die Zeitüberschreitungszeit größer als die Spin -Zeit ist, wird nach der Beurteilung des Threads der Thread für einen bestimmten Zeitraum suspendiert, wenn (sildParkafterFailedacquire (P, Knoten) && nanostime> spinfortimeoutthreshold) {// den aktuellen Thread für eine Zeitspanne, die für eine Zeitspanne und an die Zeit ist. } // Die aktuelle Zeit des Systems long Now = System.nanotime () erhalten; // Zeitüberschreitungszeit wird vom Zeitintervall der Erwerbssperrung Nanostimeout - = jetzt - letztes Mal abtrahiert; // letztes letztes letztendlich aktualisieren = jetzt; // Ausnahme wird ausgelöst, wenn eine Interrupt -Anforderung während des Erwerbs der Sperre empfangen wird, wenn (thread.interrupted ()) {neue InterruptedException () werfen; }}} schließlich {if (fehlgeschlagen) {covornacquire (node); }}}Wenn Sie die Zeitüberschreitungsempfehlung festlegen, wird zunächst das Schloss erfasst. Nach dem ersten Versagen des Erwerbs basiert er auf der Situation. Wenn die eingehende Zeitüberschreitungszeit größer als die Spin -Zeit ist, wird der Thread für einen bestimmten Zeitraum suspendiert, andernfalls wird er sich dreht. Nach jedem Erwerb des Schlosses wird die Zeitüberschreitungszeit von der Zeit zum Erwerb des Schlosses abgezogen. Bis die Zeitüberschreitung weniger als 0 beträgt, bedeutet dies, dass die Zeitüberschreitungszeit verbraucht wurde. Anschließend wird der Betrieb des Erwerbs des Schlosses gekündigt und das Flaggenversagen der Erwerbsausfall zurückgegeben. Beachten Sie, dass Sie während des Erwerbs der Sperre mit der Zeitüberschreitungszeit auf Thread -Interrupt -Anforderungen antworten können.
4. Wie füllt der Faden das Schloss und verlässt die Synchronisationswarteschlange?
// Veröffentlichungssperroperation (exklusiver Modus) öffentliche endgültige Boolesche Veröffentlichung (int arg) {// Die Kennwortsperrung drehen, um festzustellen, ob es entsperren kann, wenn (tryRelease (arg)) {// den Kopfknotenknoten H = Kopf abrufen; // Wenn der Kopfknoten nicht leer ist und der Wartezustand nicht gleich 0 ist, wecken Sie den Nachfolgerknoten if (h! = Null && h.waitstatus! } Return true; } return false;} // Weck den Nachfolgerknoten privat void UnparksuSccessor (Knotenknoten) {// Erhalten Sie den Wartezustand des angegebenen Knotens int ws = node.waitstatus; // Aktualisieren Sie den Wartezustand auf 0, wenn (ws <0) {vergleicheSetwaitStatus (Knoten, WS, 0); } // den nachfolgenden Knoten des angegebenen Knotenknotens s = node.Next; // Der Nachfolgerknoten ist leer oder der Wartezustand wird abgebrochen, wenn (s == null || swaitstatus> 0) {s = null; // den ersten Knoten beenden, der nicht von der Rückwärtsfahrtswarteschlange für (Knoten T = Tail; t! = Null && t! = Node; t = t.prev) {if (t.waitstatus <= 0) {s = t; }}} // Wach den ersten Knoten nach einem bestimmten Knoten auf, der kein Abbrechenstatus ist, wenn (s! = Null) {locksupport.unpark (s.Thread); }}Nachdem der Faden das Schloss in den Raum hält, wird er sein eigenes Geschäft erledigen. Nachdem die Arbeit erledigt ist, wird das Schloss veröffentlicht und den Raum verlassen. Die Passwortsperrung kann über die TryRelease -Methode entsperrt werden. Wir wissen, dass die Tryrelease -Methode von der Unterklasse überschrieben werden muss. Die Implementierungsregeln verschiedener Unterklassen sind unterschiedlich, was bedeutet, dass die durch unterschiedlichen Unterklassen festgelegten Passwörter unterschiedlich sind. In Reentrantlock wird beispielsweise jedes Mal, wenn die Person im Raum die Tryrelease -Methode anruft, der Staat um 1 reduziert, bis der Staat auf 0 reduziert wird, die Passwortsperrung wird geöffnet. Denken Sie darüber nach, ob dieser Vorgang so aussieht, als würden wir das Rad der Passwortsperrung ständig drehen, und die Anzahl der Räder wird jedes Mal um 1 reduziert, wenn wir ihn drehen. Countdownlatch ist ein bisschen ähnlich wie dieser, außer dass es nicht nur so ist, dass es eine Person dreht, sondern dass es eine Person umdreht und die Stärke aller darauf konzentriert, das Schloss zu öffnen. Nachdem der Faden den Raum verlässt, findet er seinen ursprünglichen Sitz, dh den Kopfknoten. Sehen Sie, ob jemand auf dem Sitz eine kleine Notiz dafür hinterlassen hat. Wenn dies der Fall ist, wird es wissen, dass jemand schläft und es bitten muss, um es zu wecken, und dann wird er diesen Thread aufwachen. Wenn nicht, bedeutet dies, dass niemand vorerst in der Synchronisationswarteschlange wartet und niemand es braucht, um aufzuwachen, damit es mit Ruhe gehen kann. Der obige Vorgang ist der Prozess der Veröffentlichung der Sperre im exklusiven Modus.
Hinweis: Alle oben genannten Analysen basieren auf JDK1.7, und es wird Unterschiede zwischen verschiedenen Versionen geben, die Leser müssen aufpassen.
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.