Durch die Analyse im vorherigen Artikel wissen wir, dass es drei Möglichkeiten gibt, Sperren mit dem ausschließlichen Modus zu erwerben, nämlich ohne Reaktions -Thread -Interrupts, um Reaktions -Thread -Interrupts zu erhalten und Zeitüberschreitungszeit zu erhalten. Es gibt auch diese drei Möglichkeiten, Schlösser im gemeinsamen Modus zu erwerben, und sie sind im Grunde genommen gleich. Wenn wir einen Weg herausfinden, können wir andere Möglichkeiten schnell verstehen. Obwohl der Quellcode für Abstractqueedsynchronizer mehr als tausend Zeilen enthält, wird er auch viele Male wiederholt, sodass die Leser am Anfang keine Angst haben sollten. Lesen Sie es einfach geduldig und langsam, Sie werden es natürlich allmählich verstehen. Nach meiner persönlichen Erfahrung gibt es einige wichtigere Aspekte, die beim Lesen des Abstractquedsynchronizer -Quellcodes, nämlich den Unterschied zwischen dem exklusiven Modus und dem gemeinsamen Modus, dem Wartezustand der Knoten und dem Verständnis der bedingten Warteschlangen, zu verstehen. Wenn Sie diese Schlüsselpunkte verstehen, ist die nachfolgende Quellcode -Lesart viel einfacher. Natürlich werden diese in meinem Artikel "Java Concurrency Series [1] ---- Abstractqueuedsynchronizer-Quellcode-Analyse eingeführt, und die Leser können sie zuerst überprüfen. Dieser Artikel analysiert den Freigabemodus in drei Möglichkeiten, um Schlösser zu erwerben, und eine Möglichkeit, Schlösser freizugeben.
1. Nicht auf Thread -Interrupt -Akquisition reagieren
// Erwerben der Sperre im nicht übertriebenen Modus (Shared-Modus) öffentliche endgültige Leerraum Erwerb (int arg) {// 1. Versuchen Sie, das Schloss zu erwerben, wenn (tryacquireshared (arg) <0) {// 2. Wenn die Akquisition fehlschlägt, geben Sie diese Methode doacquireshared (arg) ein. }}//Try to acquire the lock (shared mode)//Negative number: indicates that the acquisition failed//Zero value: indicates that the current node is successfully acquired, but the successor node can no longer obtain //Positive number: indicates that the current node is successfully acquired, and the successor node can also obtain success protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException();}Das Aufrufen der erwerbenen Methode ist eine Möglichkeit, das Schloss zu erwerben, ohne auf Thread -Interrupts zu reagieren. Bei dieser Methode wird Tryacquireshared zuerst aufgerufen, um zu versuchen, das Schloss zu erwerben. Die Methode tryacquiresharedared gibt einen Zustand des Erwerbs des Schlosses zurück. Hier gibt AQS an, dass der aktuelle Knoten, wenn der Rückgabestatus negativ ist, das Schloss nicht erfasst. Wenn 0 bedeutet, dass der aktuelle Knoten das Schloss erfasst, der nachfolgende Knoten jedoch nicht mehr erworben werden kann. Wenn es positiv ist, bedeutet dies, dass der aktuelle Knoten das Schloss erfasst und die nachfolgenden Knoten dieses Schlosses auch erfolgreich erhalten werden können. Wenn eine Unterklasse die Logik implementiert, Schlösser durch die Tryacquireshared -Methode zu erhalten, muss der Rückgabewert dieser Konvention entsprechen. Wenn der Rückgabewert des Aufrufens von Tryacquiresharedars weniger als 0 beträgt, bedeutet dies, dass der Versuch, das Schloss zu erwerben, fehlgeschlagen ist. Rufen Sie als nächstes die Methode doacquiresharedary auf, um den aktuellen Thread zur Synchronisationswarteschlange hinzuzufügen. Wir sehen die Doacquireshared -Methode.
// Get (Shared -Modus) in der Synchronisation Queue Private void doacquireshared (int arg) {// zur Synchronisation Queue Final Node Node = Addwaiter (node.shared) hinzufügen; boolean fehlgeschlagen = wahr; Versuchen Sie {boolean unterbrochen = false; für (;;) {// den Vorwärtsknoten des aktuellen Knotens Final Node p = node.Pred -Angänger () erhalten; // Wenn der Vorwärtsknoten ein Kopfknoten ist, versuchen Sie erneut, das Schloss zu erwerben, wenn (P == Kopf) {// Versuchen Sie, das Schloss erneut zu erwerben und den Erfassungsstatus zurückzugeben. erfolgreich erworbene int r = tryacquireshared (arg); if (r> = 0) {// zu diesem Ende zeigt es an, dass der aktuelle Knoten das Schloss erfolgreich erfasst hat. Zu diesem Zeitpunkt wird die Sperrstatusinformationen an den nachfolgenden Knoten SetheadandPropagate (Knoten, R) weitergegeben. P.Next = null; // Wenn eine Interrupt -Anforderung während der Thread -Blockierung empfangen wird, antworten Sie auf die Anfrage in diesem Schritt, wenn (unterbrochen) {selfterrupt (); } fehlgeschlagen = false; zurückkehren; }} // Jedes Mal, wenn die Sperraufnahme fehlschlägt, wird bestimmt, ob der Thread ausgesetzt werden kann. Wenn es möglich ist, wird der Thread in der ParkandCheckinterrupt -Methode aufgehängt, wenn (shollparkAfterfailedacquire (p, Knoten) && ParkandCheckinterrupt ()) {interbrupted = true; }}} schließlich {if (fehlgeschlagen) {covornacquire (node); }}}Wenn Sie die Doacquireshared -Methode zuerst eingeben, rufen Sie zuerst die Addwaiter -Methode auf, um den aktuellen Thread in einen Knoten einzuwickeln und am Ende der Synchronisationswarteschlange zu setzen. Wir haben über den Prozess des Hinzufügens von Knoten gesprochen, wenn wir über den exklusiven Modus gesprochen haben, also werde ich hier nicht darüber sprechen. Nachdem ein Knoten in die Synchronisationswarteschlange eingetreten ist, wenn er feststellt, dass der Knoten vor ihm der Kopfknoten ist, weil der Faden des Kopfknotens das Schloss erfasst und in den Raum eingetreten ist, ist es an der Reihe, das Schloss zu erwerben. Daher wird sich der aktuelle Knoten nicht zuerst hängen, sondern versuchen, das Schloss erneut zu erwerben. Wenn die vorne vorne nur das Schloss freigibt und verlässt, kann der aktuelle Knoten das Schloss erfolgreich erhalten. Wenn die vorne vorgeschlagene Person das Schloss nicht veröffentlicht hat, nennt sie die Methode für den SHIFTSPARKAFTERFAILEDACQUIRE. Bei dieser Methode wird der Zustand des Kopfknotens in Signal geändert. Nur wenn sichergestellt wird, dass der Status des vorherigen Knotens ein Signal ist, kann sich der aktuelle Knoten mit Zuversicht hängen. Alle Themen werden in der ParkandCheckinterrupt -Methode aufgehängt. Wenn der aktuelle Knoten das Schloss erfolgreich erfasst, wird die Methode SetheadandSpagate aufgerufen, sich selbst als Kopfknoten zu setzen und den Knoten, der auch dahinter gemeinsam genutzt wird, aufzuwecken. Werfen wir einen Blick auf den spezifischen Betrieb der Setheadand -Propagate -Methode.
// Setzen Sie den Kopfknoten und propagieren Sie den Status des Sperre (Shared -Modus) privat void SetheadandPropagate (Knotenknoten, int -Propagate) {Knoten H = Kopf; // den angegebenen Knoten als Kopfknoten Sethead (Knoten) festlegen; // Wenn die Ausbreitung größer als 0 ist, bedeutet dies, dass die Sperre erhalten kann, wenn (ausbreiten> 0 || H == NULL || H.Waitstatus <0) {// den Nachfolgeknoten des angegebenen Knotenknotens s = node.next erhalten; // Wenn der Nachfolgerknoten des angegebenen Knotens leer ist oder sein Zustand ein gemeinsamer Zustand ist, wenn (s == null || S. isshared ()) {// den Nachfolgeknoten doreleaseshared () aufwecke; }}} // Sperre Operation (Shared -Modus) private void doreleaseshared () {for (;;) {// den Kopfknoten des synchronen Warteschlangenknotens h = Kopf erhalten; if (h! = null && h! = schwanz) {// Holen Sie sich den Wartezustand des Kopfknotens int ws = h.waitstatus; // Wenn der Status des Kopfknotens ein Signal ist, bedeutet dies, dass jemand dahinter stellt if if (WS == node.Signal) {// den Wartezustand des Kopfknotens auf 0 if (! VergleicheSetwaitStatus (h, node.Signal, 0)) {Fortsetzung; } // Aufwach den Nachfolgeknoten UnparksuSccessor (H); // Wenn der Status des Kopfknotens 0 beträgt, bedeutet dies, dass niemand später an der Schlange stellt, nur den Kopfstatus so modifizieren, dass sie sich ausbreiten}, wenn (WS == 0 &&! VergleicheSetwaitStatus (h, 0, Node.Propagate)) {Fortsetzung; }} // Nur indem Sie sicherstellen, dass der Kopfknoten während des Zeitraums nicht geändert wurde, können Sie aus der Schleife ausbrechen, wenn (H == Kopf) {Break; }}}Das Aufrufen der SetheadandPropagate -Methode setzt sich zunächst als Kopfknoten fest und entscheidet dann, ob der Nachfolgerknoten auf der Grundlage des Rückgabewerts der bestandenen Tryacquireshared -Methode aufweckt wird. Wie bereits erwähnt, bedeutet dies, dass der aktuelle Knoten das Schloss erfolgreich erfasst hat und der nachfolgende Knoten das Schloss erfolgreich erfassen kann. Zu diesem Zeitpunkt muss der aktuelle Knoten den Knoten aufwecken, der sich ebenfalls im gemeinsam genutzten Modus befindet. Beachten Sie, dass jedes Mal, wenn Sie aufwachen, nur den nächsten Knoten aufwachen. Wenn sich der letztere Knoten nicht im freigegebenen Modus befindet, betritt der aktuelle Knoten direkt den Raum und weckt den weiteren Knoten nicht auf. Der Betrieb von Aufwachen nach den Nachfolgerknoten im gemeinsamen Modus wird in der doreleaSeschared -Methode durchgeführt. Die Weckvorgänge des gemeinsamen Modus und des exklusiven Modus sind im Grunde genommen gleich. Beide finden die Marke auf Ihrem Sitz (Wartezustand). Wenn die Marke Signal ist, bedeutet dies, dass jemand dazu beitragen muss, sie später aufzuwecken. Wenn die Marke 0 ist, bedeutet dies, dass zu diesem Zeitpunkt niemand in der Warteschlange stellt. Wenn Sie im exklusiven Modus feststellen, dass niemand an der Warteschlange steht, lassen Sie die Warteschlange direkt. Wenn Sie im gemeinsamen Modus feststellen, dass sich niemand hinter der Warteschlange stellt, hinterlässt der aktuelle Knoten weiterhin eine kleine Notiz, bevor er verlässt (den Warteschlange stat, um sich zu vermehren), um den späteren Personen den verfügbaren Status dieses Schlosses zu mitteilen. Wenn dann die Person, die später kommt, beurteilen kann, ob sie das Schloss basierend auf diesem Staat direkt erwerben soll.
2. Antwort auf den Erwerb von Thread -Interrupt
// Erwerben des Sperre im unterbruptiblen Modus (Shared -Modus) öffentliche endgültige void erwerben, die er Akku (int arg) unterrubt wird (// Bestimmen Sie zuerst, ob der Thread unterbrochen wird, werfen Sie eine Ausnahme aus, wenn (Thread.interrupted ()) {neue InterruptedException (); } // 1. Versuchen Sie, das Schloss zu erwerben, wenn (tryacquireshared (arg) <0) {// 2. Wenn die Akquisition fehlschlägt, geben Sie diese Methode doacquiresharedinterrupticle (ARG) ein. }} // Erfassen im unterbruptiblen Modus (Shared -Modus) private void doacquiresharedInterrupticled (int arg) löscht InterruptedException aus {// den aktuellen Knoten in den Schwanz der Synchronisation -Warteschlange Final Node Node = Addwaiter (Node.shared) einfügen; boolean fehlgeschlagen = wahr; try {for (;;) {// den vorherigen Knoten endgültig node p = node.prred -Antimer () erhalten; if (p == Kopf) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandSpropagate (Knoten, r); P.Next = null; fehlgeschlagen = falsch; zurückkehren; }} if (sildParkAfterFailedacquire (p, Knoten) && ParkandCheckinterrupt ()) {// Wenn der Thread während des Blockierungsvorgangs eine Interrupt -Anforderung erhält, wirft es sofort eine Ausnahme hier, um neue InterruptedException () zu werfen. }}} schließlich {if (fehlgeschlagen) {covornacquire (node); }}}Die Art und Weise, wie ein Schloss als Reaktion auf Thread -Interrupts und die Art und Weise, eine Sperre als Reaktion auf Faden -Interrupts zu erwerben, im Grunde genommen der gleiche erfasst. Der einzige Unterschied besteht darin, auf Thread -Interrupt -Anfragen zu reagieren. Wenn der Thread -Interrupt nicht auf den Thread -Interrupt reagiert, um das Schloss zu erwerben, wird der Faden aus der ParkandCheckinterrupt -Methode geweckt. Nach dem Aufwachen wird sofort zurückgegeben, ob die Interrupt-Anfrage eingegangen ist. Selbst wenn die Interrupt -Anfrage eingeht, wird sie sich weiter drehen, bis sie erworben wurde, bis sie auf die Interrupt -Anfrage reagiert und sich selbst hängt. Der Thread reagiert sofort auf die Interrupt -Anforderung, nachdem der Thread geweckt wurde. Wenn der Thread -Interrupt während des Blockierungsprozesses empfangen wird, wird eine InterruptedException sofort geworfen.
3. Setzen Sie die Zeitüberschreitungszeit, um zu erhalten
// Erwerben des Schlosses mit einem begrenzten Zeitüberschreitungs (Shared -Modus) öffentliches boolean -tryacquiresharednanos (int arg, long nanostimeout) löst InterruptedException aus {if (thread.interrupted ()) {neue interbruptexception () werfen; } // 1. Rufen Sie Tryacquireshared an, um zu versuchen, das Schloss zu erwerben // 2. Wenn die Akquisition fehlschlägt, rufen Sie doacquiresharednanos return telacquireshared (arg)> = 0 || doacquiresharednanos (arg, nanostimeout);} // Die Sperre mit einem begrenzten Zeitlimit (Shared -Modus) privat boolean doacquiresharednanos (int arg, long nanostimeout) erfassen, löst unterbrochene unterbrochene (lange letztes letztendlich). endgültiger Knoten node = addwaiter (node.shared); boolean fehlgeschlagen = wahr; try {for (;;) {// den vorherigen Knoten des aktuellen Knotens endgültig Knoten p = node.prred -Angänger () erhalten; if (p == Kopf) {int r = tryAcquireshared (arg); if (r> = 0) {setheadandSpropagate (Knoten, r); P.Next = null; fehlgeschlagen = falsch; zurückkehren; }} // Wenn das Timeout aufgebraucht ist, wird die Erfassung beendet und die Fehlerinformationen werden zurückgegeben, wenn (Nanostimeout <= 0) {return false; } // 1. Überprüfen Sie, ob die Bedürfnisse der Threadsuspension erfüllt sind (garantiert der Vorwärtsknotenstatus ist Signal) // 2. Überprüfen Sie, ob die Zeitüberschreitungszeit größer als die Spin -Zeit ist, wenn (sildParkAfterfailedacquire (P, Knoten) && nanostimeout> spinfortimeoutThreshold) {// Wenn die beiden oben genannten Bedingungen eingehalten werden, wird der aktuelle Thread für einen Zeitraum von Locksupport.parknanos ausgesetzt (nanostimout); } long Now = System.nanotime (); // Zeitlimitzeit jedes Mal die Zeit der Schlosserakquisition nanostimeout - = jetzt - letztes Mal; lastTime = jetzt; // Wenn während des Blockierens eine Interrupt -Anforderung empfangen wird, wird eine Ausnahme sofort ausgelöst, wenn (thread.interrupted ()) {neue InterruptedException () werfen; }}} schließlich {if (fehlgeschlagen) {covornacquire (node); }}}Wenn Sie die beiden oben genannten Erfassungsmethoden verstehen, ist es sehr einfach, die Erfassungsmethode der Zeitüberschreitungszeit festzulegen. Der Grundprozess ist der gleiche, hauptsächlich, um den Zeitüberschreitungsmechanismus zu verstehen. Wenn das Schloss zum ersten Mal erworben wird, wird die Doacquiresharednanos -Methode aufgerufen und die Zeitüberschreitungszeit wird weitergegeben. Nach dem Eintritt in die Methode wird das Schloss nach der Situation erneut erworben. Wenn das Schloss erneut ausfällt, muss der Faden als suspendiert angesehen werden. Zu diesem Zeitpunkt werden wir feststellen, ob die Zeitüberschreitungszeit größer ist als die Spin -Zeit. In diesem Fall wird der Thread für einen bestimmten Zeitraum suspendiert. Andernfalls werden wir weiterhin versuchen, es zu erhalten. Nach jedem Erwerb des Schlosses werden wir die Zeit des Schlosses abziehen, um es zu erwerben. Wir werden so so schleifen, bis die Zeitüberschreitungszeit erschöpft ist. Wenn das Schloss nicht erworben wurde, wird die Akquisition gekündigt und die Flagge der Erwerbsausfall zurückgegeben. Der Thread reagiert während des gesamten Zeitraums auf Thread -Interrupts.
V.
// Betrieb der Veröffentlichung von Sperre (Shared -Modus) öffentliches endgültiges booleanes Jahr (int arg) {//1.try zur Veröffentlichung der Sperre (tryReleaseshared (arg)) {// 2. Wenn die Veröffentlichung erfolgreich ist, wecken Sie andere Threads Doreleaseshared () auf; zurückkehren; } return false;} // Versuchen Sie, den Schloss (gemeinsam genutzter Modus) geschützt boolean tryreleaseshared (int arg) {Neue UnsupportedOperationException (); if (h! = null && h! = schwanz) {// Holen Sie sich den Wartezustand des Kopfknotens int ws = h.waitstatus; // Wenn der Status des Kopfknotens ein Signal ist, bedeutet dies, dass jemand später anhält, wenn (ws == node.Signal) {// zuerst den Wartezustand des Kopfknotens auf 0 if (! VergleicheSetwaitStatus (h, node.Signal, 0)) {Fortsetzung; } // Weck den nachfolgenden Knoten -UnparksuSccessor (H) auf; // Wenn der Status des Kopfknotens 0 beträgt, bedeutet dies, dass niemand später an der Warteschlange stellt, sondern nur den Kopfstatus ändert, um sich zu verbreiten. }} // Die Schleife kann nur gebrochen werden, wenn (h == head) {break; }}}Nachdem der Faden im Raum beendet ist, wird die freigelassene Methode zur Freigabe des Schlosses aufgerufen. Zunächst wird die tryReleaseshared -Methode aufgerufen, um zu versuchen, das Schloss freizugeben. Die Beurteilungslogik dieser Methode wird von der Unterklasse implementiert. Wenn die Veröffentlichung erfolgreich ist, rufen Sie die doreleaSeaseShared -Methode an, um den Nachfolgerknoten aufzuwecken. Nach dem Ausgehen aus dem Raum findet es den ursprünglichen Sitz (Kopfknoten) und prüft, ob jemand kleine Noten auf dem Sitz (Statussignal) hinterlassen hat. Wenn ja, wecken Sie den Nachfolgerknoten. Wenn es nein gibt (Status 0), dass niemand in der Warteschlange stellt, dann ist das Letzte, was es vor dem Ausscheiden tun muss, zu gehen, nämlich eine kleine Notiz auf seinem Sitz (Status soll sich ausbreiten), um den Menschen hinter dem Schloss zu sagen, dass er den Staat erwerben soll. Der einzige Unterschied zwischen dem gesamten Verringerungsprozess und dem exklusiven Modus besteht darin, in diesem letzten Schritt zu arbeiten.
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.