Mindmap-Galerie Java-Parallelitätssperrtool
Dies ist eine Mindmap über Java-Parallelitätssperr-Tools. Diese Parallelitätssperr-Tools können eine wichtige Rolle bei der gleichzeitigen Programmierung von Java spielen und Ihnen helfen, effizienten und sicheren gleichzeitigen Code zu schreiben.
Bearbeitet um 2024-01-18 10:28:15Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
Java-Parallelitätssperrtool
AbstractQueueSynchronizer
Einführung
Das Grundgerüst zum Erstellen von Sperren oder anderen Synchronisierungskomponenten, das hauptsächlich zur Verwaltung des Synchronisierungsstatus verwendet wird
Die wichtigste Verwendungsmöglichkeit ist die Vererbung
Die Blockierung kann nur zu einem bestimmten Zeitpunkt erfolgen, wodurch die Kosten für den Kontextwechsel reduziert werden.
erben
AbstractOwnableSynchronizer
Grundlegende Implementierung des Synchronizers, die es Threads ermöglicht, den exklusiven Modus zu besetzen
Mitgliedsvariablen
privater transienter Thread exclusiveOwnerThread
Der Thread, der sich derzeit im Synchronisierungsstatus befindet
Hauptmethode
protected final void setExclusiveOwnerThread(Thread-Thread)
Exklusiven Thread festlegen
protected final Thread getExclusiveOwnerThread()
Holen Sie sich den aktuellen exklusiven Thread
Schnittstelle implementieren
Serialisierbar
innere Klasse
statischer Endklassenknoten
Einführung
AQS verwendet intern eine bidirektionale FIFO-Synchronisationswarteschlange, um die Verwaltung des Synchronisationsstatus abzuschließen. Wenn ein Thread nicht abgerufen werden kann, wird er am Ende der Warteschlange hinzugefügt. Die Struktur der Warteschlange ist der Knoten im AQS. Die Bedingungswarteschlange verwendet ebenfalls die Definition von Node, befindet sich jedoch in einer anderen Warteschlange
Mitgliedsvariablen
statische Konstante
statischer finaler Node SHARED = neuer Node()
Zeigt an, dass sich der Knoten im gemeinsam genutzten Modus befindet
statischer Endknoten EXKLUSIV = null
Zeigt an, dass sich der Knoten im exklusiven Modus befindet und nur ein Thread gleichzeitig den Synchronisationsstatus hält.
static final int CANCELLED = 1
Der Knoten (Thread) befindet sich aufgrund einer Zeitüberschreitung oder Unterbrechung in diesem Zustand und wird anschließend nicht in andere Zustände wechseln und sollte auch nicht mehr blockiert werden.
statisches finales int SIGNAL = -1
Wenn sich der Knoten (Thread) in diesem Zustand befindet, befindet sich der Nachfolgeknoten oder wird sich in einem blockierten Zustand befinden (durch Parken). Wenn also der aktuelle Knoten die Sperre aufhebt oder aufgehoben wird, muss Unpark aufgerufen werden, um den Nachfolgeknoten aufzuwecken .
static final int BEDINGUNG = -2
Zeigt an, dass sich der aktuelle Knoten (Thread) in der Bedingungswarteschlange befindet (über Warten) und nicht Teil der Synchronisationswarteschlange ist, bis der Status zu einem bestimmten Zeitpunkt auf 0 gesetzt wird (über Signal).
static final int PROPAGATE = -3
Gemeinsame Sperren weitergeben (vom Hauptknoten verwendet)
volatile int waitStatus
Einschließlich der obigen Angaben CANCELLED/SIGNAL/CONDITION/PROPAGATE und 0 (was anzeigt, dass er sich nicht in diesen vier Zuständen befindet) zeigt eine nicht negative Zahl an, dass der Knoten keine Interaktion erfordert
volatiler Knoten vorher
Vorläuferknoten
flüchtiger Knoten als nächstes
Nachfolgeknoten
flüchtiger Thread-Thread
Der Thread, der dem Knoten entspricht
Knoten nextWaiter
In der Synchronisationswarteschlange gibt es an, ob sich der Knoten in einem gemeinsam genutzten oder exklusiven Zustand befindet, gleich „SHARED“, um die gemeinsame Nutzung anzuzeigen, und null, um „exklusiv“ anzugeben; in der bedingten Warteschlange gibt es einen Verweis auf den nächsten Knoten an.
Die Bedingung kann nur verwendet werden, wenn sich die Synchronisierungswarteschlange im exklusiven Modus befindet. Daher ist diese Variable für die gemeinsame Nutzung konzipiert.
Hauptmethode
Konstruktionsmethode
Knoten()
Wird zum Einrichten von Anfangsknoten oder zum Erstellen von SHARED-Knoten verwendet
Knoten (Thread-Thread, Knotenmodus)
Zur Verwendung in addWaiter
Node(Thread-Thread, int waitStatus)
Im gebrauchten Zustand
Member-Methode
letzter boolescher Wert isShared()
return nextWaiter == SHARED
letzter Knoten-Vorgänger()
Rufen Sie den Vorgängerknoten ab und lösen Sie eine Ausnahme aus, wenn er null ist
öffentliche Klasse ConditionObject
Einführung
Die Implementierung der Condition-Schnittstelle dient der grundlegenden Lock-Implementierung. Verwenden Sie Node, um eine Warteschlange zu erstellen. Jedes Bedingungsobjekt enthält eine FIFO-Warteschlange (einseitig).
Die Warteschlange verwendet die Eigenschaft nextWaiter des Knotens als Referenz auf den nächsten Knoten.
Schnittstelle implementieren
Zustand
Serialisierbar
Mitgliedsvariablen
statische Konstante
privates statisches finales int REINTERRUPT = 1
Zeigt die Notwendigkeit an, beim Verlassen der Warteschleife erneut zu unterbrechen
privates statisches final int THROW_IE = -1
Gibt an, dass eine InterruptedException ausgelöst werden muss, wenn die Wartefunktion verlassen wird
privater transienter Knoten firstWaiter
Zeigt auf den Kopfknoten der bedingten Warteschlange
privater transienter Knoten lastWaiter
Zeigt auf den Endknoten der Bedingungswarteschlange
Hauptmethode
öffentliches Finale voidwait()
Lassen Sie den aufrufenden Thread in die Warteschlange eintreten, geben Sie die Sperre frei und geben Sie ihn in den Blockierungsstatus ein. Versuchen Sie, die Sperre zu erhalten, nachdem er durch einen Interrupt aktiviert wurde.
privater Knoten addConditionWaiter()
Fügen Sie der Warteschlange einen neuen Knoten hinzu.
Spezifische Implementierungslogik: Wenn festgestellt wird, dass lastWaiter abgebrochen wurde, rufen Sie unlinkCancelledWaiters auf, um die abgebrochenen Methoden in der gesamten Warteschlange zu löschen (sowohl firstWaiter als auch lastWaiter können auf neue Knoten verweisen) und dann einen neuen Knoten hinzufügen (CONDITION-Status). Ende der Warteschlange
private void unlinkCancelledWaiters()
Verwenden Sie while, um Knoten zu löschen, deren waitStatus ab firstWaiter nicht CONDITION ist. Da der Methodenaufruf erfolgt, bevor die Sperre aufgehoben wird, muss der Schleifenprozess nicht gesperrt werden. Um Rückstände im GC zu vermeiden, wenn kein Signal vorhanden ist, wird diese Methode nur verwendet, wenn eine Zeitüberschreitung oder ein Abbruch auftritt.
Der waitStatus des Knotens in der Bedingungswarteschlange sollte nur zwei Zustände haben: CONDITION und CANCELLED.
final int fullRelease(Knotenknoten)
Wenn der Sperrstatus nicht freigegeben werden kann, wird eine IllegalMonitorStateException ausgelöst und der Knoten wird in den Status CANCELLED versetzt. Bei Erfolg wird der Statuswert vor der Freigabe zurückgegeben.
freigeben
Hier wird die Freigabemethode von AQS aufgerufen und ein geeigneter Knoten zum Aufwachen gefunden.
Die Parameter verwenden das Statusattribut von AQS
letzter boolescher Wert isOnSyncQueue(Knotenknoten)
Bestimmen Sie, ob sich der Knoten in der Synchronisationswarteschlange befindet (beachten Sie, dass es sich um eine Synchronisationswarteschlange und nicht um eine bedingte Warteschlange handelt): Wenn der WaitStatus CONDITION ist, bedeutet dies, dass er sich nicht in der Synchronisationswarteschlange befindet null bedeutet auch, dass es sich nicht in der Synchronisationswarteschlange befindet (diese beiden Attribute werden für die Synchronisationswarteschlange verwendet. Der Vorgänger und Nachfolger der bedingten Warteschlange verwenden diese beiden Attribute nicht) und dann die Methode findNodeFromTail aufrufen, um die Synchronisation zu durchlaufen Warteschlange vom Endknoten, um zu sehen, ob sich der Knoten darin befindet.
Die Beziehung zwischen Synchronisationswarteschlange und Bedingungswarteschlange: Der Sperrenwettbewerb basiert nur auf der Synchronisationswarteschlange und speichert nur Threads, die aufgrund fehlender Bedingungen blockiert sind. Wenn Threads um Sperren konkurrieren müssen, müssen sie dennoch in die Synchronisationswarteschlange konvertiert werden.
Diese Methode wird als Beurteilungsbedingung für while im Code verwendet. Wenn sich der Knoten nicht in der Synchronisationswarteschlange befindet (was anzeigt, dass es sich nicht um signal/signalAll handelt), tritt er in den while ein und blockiert ihn durch Parken. Wenn der Knoten geweckt wird, stellt er zunächst fest, ob er durch einen Interrupt geweckt wurde. Wenn nicht, kehrt er zum While-Prozess zurück. Andernfalls wird versucht, den Knoten zur Synchronisationswarteschlange hinzuzufügen und aus dem While auszubrechen Verfahren.
AcquireQueued(Knoten, gespeicherter Zustand)
Es wird ständig versucht, den Synchronisationsstatus „savedState“ für die Knoten in der Synchronisationswarteschlange abzurufen
unlinkCancelledWaiters
Wenn node.nextWaiter nach dem Erwerb der Sperre nicht null ist, rufen Sie diese Methode auf, um den Knoten im abgebrochenen Zustand zu löschen.
reportInterruptAfterWait
Wenn es zuvor aufgrund eines Interrupts aktiviert wurde, muss der Interrupt basierend auf den Ergebnissen der vorherigen Beurteilung verarbeitet werden, ob der Interrupt-Status zurückgesetzt oder eine Interrupt-Ausnahme ausgelöst werden soll.
öffentliches Finale langes WartenNanos(long nanosTimeout)
Die Grundlogik ist ähnlich wie beim Warten, mit dem Zusatz einer Timeout-Beurteilung.
Ich verstehe den letzten Teil von transferAfterCancelledWait nicht ganz.
öffentliches finales boolesches Warten (lange Zeit, TimeUnit-Einheit)
Die Grundlogik ist ähnlich wie bei waitingNanos, Sie können die Zeiteinheit angeben und diese wird schließlich zur Berechnung in Nanosekunden umgewandelt.
öffentliche endgültige Leere warten aufUnunterbrochen ()
Die Grundlogik ähnelt dem Warten und reagiert nicht auf Interrupts (dh selfInterrupt setzt den Interrupt-Status zurück).
public final boolean waitingUntil(Datumsfrist)
Die Grundlogik ist ähnlich wie beim Warten, allerdings mit der Hinzufügung einer maximalen Blockierungszeit.
öffentliches endgültiges Void-Signal()
Verschieben Sie den am längsten wartenden Thread aus der bedingten Warteschlange in die Synchronisationswarteschlange
protected boolean isHeldExclusively()
Gibt zurück, ob der aktuelle Thread die Sperre im exklusiven Modus hält. Wenn ja, wird true zurückgegeben. Diese Methode befindet sich in AQS und stellt keine spezifische Implementierung bereit. Da diese Methode jedoch nur in der Bedingung verwendet wird, muss sie nicht implementiert werden, wenn ConditionObject nicht verwendet wird, andernfalls muss sie implementiert werden.
private void doSignal(Knoten zuerst)
Entfernen Sie die Knoten aus der Bedingungswarteschlange und konvertieren Sie sie in Knoten aus der Synchronisationswarteschlange. Der Implementierungsprozess ist eine Schleife von vorne nach hinten, bis er auf einen Knoten trifft, der nicht null ist und sich nicht im Status CANCELED befindet. Konvertieren Sie ihn und verlassen Sie die Schleife
final boolean transferForSignal(Node node)
Konvertieren Sie den Knoten von der bedingten Warteschlange in die Synchronisationswarteschlange und geben Sie zurück, ob die Konvertierung erfolgreich ist. Die Implementierungslogik besteht darin, zunächst zu bestimmen, ob der Knotenstatus „BEDINGT“ ist. Geben Sie dann „false“ zurück, um in die Synchronisationswarteschlange einzutreten und den Vorgängerknoten des Knotens in der Warteschlange abzurufen Status des Vorgängerknotens ist > 0 oder CAS ist geändert. Wenn waitStatus fehlschlägt (Änderungen aufgetreten sind), entparken Sie den Thread und geben Sie schließlich true zurück
Er wird nicht unbedingt direkt nach dem Beitritt zur Synchronisationswarteschlange aktiviert. Nur wenn waitStatus>0 oder sich während der CAS-Ausführung ändert, wird der Thread entparkt und aktiviert. Wenn Sie zu diesem Zeitpunkt nicht aufwachen, warten Sie, bis die Logik der Synchronisationswarteschlange aktiviert ist, und wachen dann auf.
enq
öffentliches endgültiges void signalAll()
Übertragen Sie alle qualifizierten (nicht abgebrochenen) Knoten in der Bedingungswarteschlange in die Synchronisationswarteschlange. Die Grundlogik ähnelt der von Signal, der Unterschied besteht darin, dass die Methode transferForSignal in einer Schleife ausgeführt wird
isHeldExclusively
private void doSignalAll(Knoten zuerst)
transferForSignal
protected final boolean hasWaiters()
Unabhängig davon, ob sich Threads in der Bedingungswarteschlange befinden, besteht die Implementierungsmethode darin, die Bedingungswarteschlange von Anfang an zu durchlaufen. Wenn es einen Knoten mit dem Status CONDITION gibt, wird true zurückgegeben, andernfalls wird false zurückgegeben.
Mitgliedsvariablen
statische Variable
static final long spinForTimeoutThreshold = 1000L;
Der für die Zeitüberschreitung verwendete Schwellenwert ist nicht erforderlich, um „Parken mit Zeitüberschreitung“ aufzurufen, sondern die Schleife weiter auszuführen. Zu diesem Zeitpunkt ist die Spin-Effizienz höher
privat statisch final Unsicher unsicher
Unsafe.getUnsafe(), Folgendes wird verwendet, um den Offset über die Methode objectFieldOffset zu erhalten, was spätere Änderungen direkt über die CAS-Operation von Unsafe erleichtert.
privater statischer finaler langer StateOffset
AQS-Zustand
privater statischer finaler langer HeadOffset
AQS-Leiter
privater statischer finaler langer TailOffset
AQS-Schwanz
privater statischer finaler langer WaitStatusOffset
Wartestatus des Knotens
private static final long nextOffset
Knoten als nächstes
privater, vorübergehender, flüchtiger Knotenkopf
Der Hauptknoten der Synchronisationswarteschlange, verzögertes Laden, wird nur durch setHead geändert. Wenn der Hauptknoten vorhanden ist, kann sein Status nicht ABGEBROCHEN werden
privater, vorübergehender, flüchtiger Knotenschwanz
Der Endknoten der Synchronisationswarteschlange, verzögertes Laden
privater volatiler int-Zustand
Synchronisierungsstatus
Hauptmethode
protected final int getState()
Gibt den aktuellen Wert des Synchronisierungsstatus zurück
protected final void setState(int newState)
Aktuellen Synchronisierungsstatus festlegen
protected final boolean CompareAndSetState(int Expect, Int Update)
CAS legt den Status fest und die unterste Ebene ruft die CAS-Methode von Unsafe auf.
privater Knoten addWaiter (Knotenmodus)
Enqueue-Methode
Fügen Sie den aktuellen Thread im angegebenen Modus am Ende der Warteschlange hinzu
Das ist erwähnenswert
Der Parameter Node wird verwendet, um den Modus anzugeben, und der Thread wird über currentThread abgerufen
Wenn der Schwanz in der Implementierung nicht leer ist, versuchen Sie schnell, den Schwanz festzulegen. Bei Erfolg wird die Methode enq aufgerufen.
privater Knoten enq (letzter Knotenknoten)
Schleife (Spin) CAS. Wenn der Schwanz leer ist, initialisiert CAS zuerst den Kopf (neuen Knoten). Wenn der Schwanz nicht leer ist, setzt CAS den Schwanz.
Der Parameterknoten ist hier der Knoten, der hinzugefügt werden muss, und nicht der Modus.
öffentlicher finaler boolescher Wert hasQueuedPredecessors()
Bestimmen Sie, ob vor dem aktuellen Thread ein Knoten vorhanden ist. Wenn ja, geben Sie true zurück, andernfalls geben Sie false zurück. Wird zur Implementierung von Messeschlössern verwendet. Spezifische Implementierung: Wenn die Warteschlange nicht leer ist, wird true zurückgegeben, wenn festgestellt wird, dass der Nachfolgeknoten des Kopfes leer ist oder nicht der aktuelle Thread.
Wann ist head.next null?
öffentlicher finaler boolescher Wert hasQueuedThreads()
return head != tail; Gibt an, ob Threads in der Warteschlange warten
public final boolean isQueued(Thread-Thread)
Unabhängig davon, ob sich der Thread in der Warteschlange befindet, erfolgt die Implementierung in einer Schleife vom Ende zum Anfang
public final int getQueueLength()
Ermitteln Sie die Länge der Warteschlange. Die Implementierungsmethode besteht darin, eine Schleife vom Ende zum Anfang durchzuführen, um die Anzahl der Knoten zu berechnen, deren Thread nicht null ist.
öffentliche endgültige Sammlung<Thread> getQueuedThreads()
Ruft die Sammlung von Threads in der Warteschlange ab und gibt sie in Form einer Sammlung zurück. Die Implementierung besteht darin, eine ArrayList zu erstellen, eine Schleife vom Ende zum Anfang durchzuführen, um Threads hinzuzufügen, die nicht null sind, und dann das ArrayList-Objekt zurückzugeben.
public final boolean besitzt (ConditionObject-Bedingung)
Ob das Bedingungsobjekt zum aktuellen Synchronizer-AQS gehört
Exklusivmodus
Es kann jeweils nur ein Thread den Synchronisationsstatus erhalten
öffentliche endgültige Leere erwerben(int arg)
Der exklusive Modus ruft den Synchronisationsstatus ab und ignoriert Unterbrechungen. Rufen Sie zunächst tryAcquire auf, um zu versuchen, die exklusive Sperre zu erhalten, und rufen Sie nach einem Fehler acquireQueued(addWaiter(Node.EXCLUSIVE), arg) auf, um zu versuchen, den Thread zur Synchronisationswarteschlange hinzuzufügen und zu ermitteln Knotenstatus und dann basierend auf dem zurückgegebenen Ergebnis (ob vorhanden) (Thread-Interrupt) Rufen Sie die Thread-Interrupt-Methode auf, um den Interrupt-Status wiederherzustellen
Der Erwerb selbst reagiert nicht auf Interrupts. Die Verarbeitung von Interrupts besteht daher darin, den Interrupt-Status wiederherzustellen
protected boolean tryAcquire(int arg)
Versuchen Sie, eine exklusive Sperre zu erhalten, und geben Sie bei Erfolg true und bei Fehlschlag false zurück. Die spezifische Implementierung wird durch Unterklassen abgeschlossen, und beim Abrufen des Synchronisationsstatus muss die Thread-Sicherheit gewährleistet sein.
final boolean acquireQueued(final Node node, int arg)
arg kann als eine Ressource verstanden werden, die intern durch den Status von AQS dargestellt werden muss, aber tatsächlich wird die spezifische Verwendung vom Entwickler definiert.
Versuchen Sie kontinuierlich, Sperren für Threads zu erhalten, die sich bereits in der Warteschlange befinden (Knoten wurde von addWaiter erstellt).
Implementierungslogik: Drehen Sie wie folgt: Wenn der Vorgängerknoten der Kopf ist und die Sperre erfolgreich erhält, setzen Sie den aktuellen Knoten auf den Kopf und die Rückkehr wird nicht unterbrochen. Fahren Sie andernfalls mit den folgenden Vorgängen fort: Rufen Sie ShouldParkAfterFailedAcquire auf, um den Knotenstatus zu ermitteln und festzulegen Rufen Sie parkAndCheckInterrupt auf, um den Thread zu blockieren und den Interrupt-Status zu überprüfen. Wenn während des Vorgangs eine Ausnahme auftritt, rufen Sie cancelAcquire auf, um die Erfassung abzubrechen.
Selbst wenn die Sperre nicht erworben wird, wird die Schleife nicht gestoppt. Wenn der Status des Vorgängerknotens erfolgreich festgelegt und der Thread durch Parken unterbrochen wird, wird der Thread angehalten. Der Knoten nach dem Kopf verwendet tryAcquire, um mit dem neuen Thread um die Sperre zu konkurrieren, was darauf hinweist, dass die Implementierung der Sperre hier unfair ist.
private void setHead(Knotenknoten)
Setzen Sie den Knoten auf „head“ und setzen Sie die Attribute „thread“ und „prev“ auf null, um die GC zu erleichtern. Tatsächlich ist es gleichbedeutend mit dem Herausnehmen aus der Warteschlange.
privater statischer boolescher Wert ShouldParkAfterFailedAcquire(Node pred, Node node)
Überprüfen und aktualisieren Sie den Status des Vorgängerknotens des Knotens, der die Sperre nicht auf SIGNAL erhalten konnte, und geben Sie zurück, ob der aktuelle Thread blockiert werden muss.
Implementierungslogik: Bestimmen Sie den Wartestatus des Vorgängerknotens und geben Sie „true“ zurück, wenn er SIGNAL ist. Wenn > 0, bedeutet dies, dass der Vorgängerknoten abgebrochen wurde und der Vorgängerknoten in einer Schleife übersprungen wird, bis der Wartestatus eines Vorgängerknotens erreicht ist <=0, und geben Sie dann false zurück; andernfalls setzt CAS den waitStatus des Vorgängerknotens auf SIGNAL und gibt false zurück
privater finaler boolescher Wert parkAndCheckInterrupt()
Rufen Sie Parken an, überprüfen Sie den Interrupt-Status und kehren Sie zurück
return thread.interrupted() Das hier zurückgegebene Ergebnis wird verwendet, um zu bestimmen, ob der Thread aufgrund eines Interrupts aktiviert wurde. Wenn er aufgrund eines Interrupts aktiviert wurde, befindet er sich immer noch in der Schleife in acquireQueued und wird erneut durch Parken blockiert. weil die interrupted()-Methode das Interrupt-Flag löscht, sodass das Parken wieder wirksam werden kann; wenn es durch Unparken aktiviert wird, wird die Sperre beim Pseudo-Aktivieren erworben, überprüfen Sie es einfach erneut durch die äußere Schleife
Was verursacht falsche Erregung?
private void cancelAcquire(Knotenknoten)
Wird aufgerufen, wenn die gesamte Methode eine Ausnahme auslöst, den Thread aus dem Knoten leeren, den Status auf CANCELLED setzen und den Knoten mit waitStatus<=0 als Vorläufer finden, sich selbst aus der Warteschlange entfernen und bei Bedarf den folgenden Knoten aktivieren. einen geeigneten Knoten
private void unparkSuccessor(Knotenknoten)
Es ist zu beachten, dass, wenn der nächste Knoten nicht leer ist, er entparkt wird. Wenn der nächste Knoten null ist, beginnen Sie am Ende und suchen Sie den vordersten Knoten mit waitStatus<=0, der nicht der Knoten vom Ende bis ist vorne ausparken
Der Grund, warum wir von hinten nach vorne suchen, liegt darin, dass das Festlegen des Vorgängers und Nachfolgers einer doppelt verknüpften Liste keine atomare Operation ist. Es kann vorkommen, dass der nächste Knoten leer ist, sich aber bereits in der Warteschlange befindet, oder dass der Knoten gerade leer ist wurde abgebrochen und sein nächster Punkt zeigt auf sich selbst, und die Durchquerung kommt gerade zu diesem Knoten
öffentliche endgültige Leere erwerbenInterruptably(int arg)
Um den Synchronisierungsstatus im exklusiven Modus als Reaktion auf eine Unterbrechung zu erhalten, wird Thread.interrupted() verwendet, um vor tryAcqure zu bestimmen, ob die Synchronisierung unterbrochen ist. Wenn ja, wird eine Ausnahme ausgelöst.
Versuchen Sie es
probiere es erstmal einmal aus
private void doAcquireInterruptably(int arg)
Die Grundlogik ist dieselbe wie bei acquireQueued. Der Unterschied besteht darin, dass addWaiter intern aufgerufen wird und nicht zurückgibt, ob eine Thread-Unterbrechung vorliegt, sondern direkt eine InterruptedException auslöst, nachdem überprüft wurde, ob der Park durch die Unterbrechung aktiviert wurde.
cancelAcquire
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
Exklusive Erfassung des Synchronisationsstatus mit Timeout, Reaktion auf Interrupts. Wenn der Synchronisierungsstatus nicht innerhalb des Timeout-Zeitraums erreicht wird, wird „false“ zurückgegeben.
Versuchen Sie es
probiere es erstmal einmal aus
privater boolescher Wert doAcquireNanos(int arg, long nanosTimeout)
Die Grundlogik ist dieselbe wie bei doAcquireInterruptably, mit der Hinzufügung einer Zeitbeurteilungslogik und der Verwendung des Schwellenwerts spinForTimeoutThreshold.
cancelAcquire
öffentliche endgültige boolesche Veröffentlichung (int arg)
Der Exklusivmodus gibt den Synchronisierungsstatus frei. Zuerst wird tryRelease aufgerufen, um zu versuchen, den Status zu ändern. Nach Erfolg wird festgestellt, dass head != null und head.waitStatus!=0 (der waitStatus von head ist hier 0, was auf eine leere Warteschlange hinweist). Ist 0, bedeutet dies, dass unparkSuccessor aufgerufen wird, um die nachfolgenden Knoten aufzuwecken, und dann true zurückgibt.
protected boolean tryRelease(int arg)
Versuchen Sie, den Status zu ändern. Es gibt keine spezifische Implementierung in AQS und Unterklassen müssen diese selbst implementieren. Für diese Methode wird die Variable arg der Release-Methode verwendet. Der Rückgabewert gibt an, ob der Status erfolgreich freigegeben wurde
Eine Beschreibung von arg finden Sie im Implementierungsabschnitt unten.
unparkSuccessor
Teilen-Modus
Mehrere Threads erhalten gleichzeitig den Synchronisierungsstatus
public final void acquireShared(int arg)
Der Shared-Modus erfasst den Synchronisierungsstatus und ignoriert Interrupts. Implementierung: Rufen Sie zuerst tryAcquireShared auf, um zu versuchen, den Synchronisierungsstatus abzurufen. Rufen Sie nach einem Fehler (das Ergebnis ist kleiner als 0) doAcquireShared auf, um den Synchronisierungsstatus abzurufen.
protected int tryAcquireShared(int arg)
Versuchen Sie, den Synchronisationsstatus abzurufen. In AQS ist keine spezielle Methode vorgesehen, diese wird von Unterklassen implementiert. Ein Rückgabewert kleiner als 0 zeigt an, dass die Erfassung fehlgeschlagen ist. Der Wert 0 zeigt an, dass die Erfassung erfolgreich war. Ein Wert größer als 0 zeigt an, dass die Erfassung erfolgreich war und andere Erfassungen im Freigabemodus ebenfalls erfolgreich waren.
private void doAcquireShared(int arg)
Nach dem Beitritt zur Warteschlange versucht Spin, den Synchronisationsstatus abzurufen. Die Grundlogik ist im Wesentlichen dieselbe wie bei „acquireQueued“ im exklusiven Modus. Der Unterschied besteht darin, dass das von tryAcquireShared zurückgegebene Ergebnis beurteilt werden muss Er ist größer oder gleich 0 (was darauf hinweist, dass noch Ressourcen vorhanden sind und die Ausbreitung fortgesetzt werden kann). Rufen Sie dann setHeadAndPropagate auf, um die Kopf- und Ausbreitungsattribute festzulegen (setzen Sie einen neuen Kopf und beurteilen Sie den Nachfolgeknoten und aktivieren Sie ihn gegebenenfalls). ; Andernfalls müssen Sie weiterhin ShouldParkAfterFailedAcquire und ParkAndCheckInterrupt für nachfolgende Vorgänge (Parken und anschließende Interrupt-Beurteilung usw.) aufrufen. Darüber hinaus wird in dieser Methode auch die selfTninterrupt-Methode zum Setzen des Interrupt-Status ausgeführt.
tryAcquireShared
Wenn der Vorgänger des Knotens der Kopf ist, versuchen Sie es zuerst.
private void setHeadAndPropagate(Node node, int propagate)
Die Verkörperung der Kommunikation. Was diese Methode tatsächlich prüft, ist der Nachfolgeknoten des Knotens, der derzeit die Sperre erhält, dh er kann einmal rückwärts weitergegeben werden. Am Anfang habe ich mich gefragt, warum ich keinen Code gefunden habe, der dem Aufwachen in einer Schleife ähnelt. Später habe ich festgestellt, dass setHeadAndPropagate selbst in for(;;) ist, wenn er die Sperre erhält , also Dadurch wird der Zweck der Rückwärtsausbreitung nacheinander erreicht. Beachten Sie, dass sie nicht gemeinsam erweckt werden und dann gegeneinander antreten, sondern einer nach dem anderen.
Wecken Sie die Knoten weiterhin rückwärts auf. Diese Methode wird nur aufgerufen, wenn tryAcquireShared das Ergebnis r>=0 zurückgibt (was anzeigt, dass noch Ressourcen verfügbar sind) und r als Propagate-Parameter an die Methode übergeben wird. Diese Methode setzt den Knoten auf „Kopf“ und bestimmt dann, ob er rückwärts übergeben werden kann. Rufen Sie in diesem Fall doReleaseShared auf, um den nachfolgenden Knoten aufzuwecken.
Urteilslogik: propagate>0 (zeigt an, dass eine Berechtigung vorliegt) Oder vorheriger Kopf == null Oder vorheriger head.waitStatus < 0 Oder der aktuelle Kopf (also der Knoten) == null Oder jetzt head.waitStatus < 0 Fahren Sie dann mit dem nächsten Schritt fort: node.next == null oder node.next.isShared()
Eigentlich ist es nicht ganz klar: 1. Warum müssen wir den aktuellen Kopf bestimmen ((h = Kopf) == null) 2. Warum doReleaseShared nach node.next == null immer noch erforderlich ist
Spekulation von 1: Da der Kopf während des Beurteilungsprozesses möglicherweise von anderen Threads geändert wird, können Sie immer noch fortfahren, wenn der neue Kopf diese Bedingung erfüllt. Ich habe das Gefühl, dass die Beurteilungslogik hier eher so ist, als ob ich nicht weiß, warum der Kopf null ist. aber ich versuche es trotzdem mit einer konservativen Verarbeitungsstrategie, dann könnte 2 eine ähnliche konservative Strategie sein
Es wird nur festgestellt, dass waitStatus <0 darauf zurückzuführen ist, dass sich der Kopfstatus möglicherweise in SIGNAL ändert, und CONDITION wird hier nicht angezeigt.
private void doReleaseShared()
Der Spin weckt den Nachfolgeknoten, der sich vom unabhängigen Modus dadurch unterscheidet, dass er auch die Ausbreitungseigenschaften sicherstellen muss. Implementierung: Schleife: Wenn die Warteschlange nicht leer ist, bestimmen Sie den WaitStatus von Head. Wenn er gleich SIGNAL ist, setzt CAS den WaitStatus auf 0. Bei Erfolg wird unparkSuccessor(head) aufgerufen, um den Nachfolgeknoten aufzuwecken , beginnen Sie von vorne; waitStatus ist 0 und CAS setzt waitStatus auf 0. Wenn PROPAGATE fehlschlägt, beginnen Sie von vorne, um festzustellen, ob der Kopfknoten geändert wurde, und wenn ja, verlassen Sie die Schleife.
Beachten Sie, dass diese Methode keine Parameter übergibt und direkt am Kopf beginnt.
Spekulation: Der Shared-Mode-Knoten darf nur drei Zustände haben: 0 (gerade erstellt), PROPAGATE und SIGNAL.
unparkSuccessor
cancelAcquire
public final void acquireSharedInterruptably(int arg)
Der Shared-Modus erhält den Synchronisationsstatus und unterstützt Interrupts auf ähnliche Weise wie der Exklusivmodus.
tryAcquireShared
private void doAcquireSharedInterruptably(int arg)
Ähnlich wie bei der doAcquireShared-Logik wird eine Ausnahme ausgelöst, nachdem eine Unterbrechung erkannt wurde
cancelAcquire
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
Der Shared-Modus mit Timeout-Zeitraum erhält den Synchronisierungsstatus und unterstützt Unterbrechungen.
tryAcquireShared
privater boolescher Wert doAcquireSharedNanos(int arg, long nanosTimeout)
Die Timeout-Beurteilungslogik ähnelt dem exklusiven Modus, und die andere Ausführungslogik ähnelt doAcquireShared
cancelAcquire
public final boolean releaseShared(int arg)
Der freigegebene Modus gibt den Synchronisierungsstatus frei
protected boolean tryReleaseShared(int arg)
Eine leere Methode, die von Unterklassen implementiert wird, versucht, den Synchronisationsstatus freizugeben. arg ist der Parameter von releaseShared und kann von Ihnen selbst definiert werden; der Rückgabewert ist true, wenn diese Methode den Synchronisationsstatus erfolgreich freigibt und andere Threads ihn abrufen können, andernfalls gibt er false zurück
doReleaseShared
Hier wecken wir immer noch nur einen nachfolgenden Knoten, und die anschließende Weckausbreitung wird vom geweckten Knoten durchgeführt.
So implementieren Sie Ihr eigenes Schloss auf Basis von AQS
konkurrierende Ressourcen
AQS verwendet den Status der privaten Variablen, um die Ressourcen für den Sperrenwettbewerb abstrakt darzustellen (z. B. 1 bedeutet belegt, 0 bedeutet nicht belegt).
Operationen am Zustand erfordern die Verwendung relevanter Methoden: getState, setState, CompareAndSetState usw.
On-Demand-Implementierungsmethode
tryAcquire, tryAcquireShared, tryRelease, tryReleaseShared, isHeldExclusively
Der Parameter arg verschiedener Methoden in AQS wird letztendlich an diese Methoden übergeben, sodass diese Methoden bestimmen, ob der Parameter arg erforderlich ist und wie er verwendet wird. Dies ist die Bedeutung von „kann alles darstellen“ in den Quellcodekommentaren.
Exklusive Sperre (unabhängiger Modus)
protected boolean tryAcquire(int arg);
Die Implementierung von Abfragen und die Beschaffung wettbewerbsfähiger Ressourcen erfolgt in der Regel durch die CAS-Operation des Staates. Die eingehenden Parameter können vom Entwickler definiert werden.
Einfaches Beispiel: protected boolean tryAcquire(int acquires) { Assert Acquires == 1; // Definieren, dass der eingehende Parameter nur 1 sein kann if (compareAndSetState(0, 1)) { return true; } falsch zurückgeben; } protected boolean tryRelease(int releases) { Assert Releases == 1; // Definieren Sie, dass der eingehende Parameter nur 1 sein kann if (getState() == 0) throw new IllegalMonitorStateException(); setState(0); return true; }
protected boolean tryRelease(int arg);
Um konkurrierende Ressourcen freizugeben, wird normalerweise die Statusvariable dekrementiert oder auf Null gelöscht. Die eingehenden Parameter können vom Entwickler definiert werden.
Innerhalb der Methode sollte es keine Blockierungen oder Wartezeiten geben
gemeinsame Sperre
protected int tryAcquireShared(int acquires)
Die eingehenden Parameter werden vom Entwickler definiert und können als die Anzahl der Ressourcen betrachtet werden, die er erhalten möchte. Der Rückgabewert int kann als die verbleibende Anzahl der gemeinsam genutzten Ressourcen betrachtet werden.
Einfaches Beispiel: protected int tryAcquireShared(int acquires) {//non-fair für (;;) { int verfügbar = getState(); int verbleibend = verfügbar – erwirbt; if (remaining < 0 || vergleichenAndSetState(verfügbar, verbleibend)) Rückkehr verbleibend; } } protected boolean tryReleaseShared(int releases) { für (;;) { int current = getState(); int next = aktuelle Versionen; if (compareAndSetState(current, next)) return true; } }
protected boolean tryReleaseShared(int releases)
Die eingehenden Parameter werden von den Entwicklern selbst definiert und können als die Anzahl der Ressourcen angesehen werden, die sie freigeben möchten.
Bedingungsvariable
Obwohl die Implementierungslogik von AQS keinen Status verwendet, wird der Status bei Verwendung von Bedingungsvariablen standardmäßig als eingehender Parameter der Freigabe (int arg) verwendet, sodass es bei der Implementierung dieser Methoden häufig erforderlich ist, die Statusvariable zu ändern.
protected boolean isHeldExclusively()
Gibt zurück, ob der aktuelle Thread ausschließlich die Mutex-Sperre belegt, die der Bedingungsvariablen entspricht
Gängige Implementierungsmethoden: protected boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } protected boolean tryAcquire(int acquires) { ... setExclusiveOwnerThread(Thread.currentThread()); return true; }
ReentrantReadWriteLock
ReentrantLock
Einführung
Wiedereintrittssperren werden in zwei Methoden unterteilt: faire Sperren und unfaire Sperren.
Schnittstelle implementieren
Sperren
Serialisierbar
innere Klasse
abstrakte statische Klassensynchronisierung
Die abstrakte Klasse ist die Grundlage für die Implementierung von ReetrantLock
erben
AbstractQueuedSynchronizer
Hauptmethode
abstrakte leere Sperre()
Ist es eine abstrakte Methode zum Erwerb einer Sperre? Muss sie von einer Unterklasse implementiert werden?
final boolean nonfairTryAcquire(int acquires)
Unfaire tryLock-Implementierung Die spezifische Implementierung lautet: CAS erhält die Sperre, wenn derzeit kein Thread die Sperre erhält. Bei Erfolg wird der sperrenexklusive Thread auf den aktuellen Thread festgelegt. Wenn der aktuelle Thread bereits die Sperre hält, wird die Anzahl der aktuellen Sperren aktualisiert. In diesen beiden Fällen wird „true“ zurückgegeben, was darauf hinweist, dass die Sperre gehalten wird. In anderen Fällen wird „false“ zurückgegeben.
Warum sollte also eine unfaire Implementierung in der übergeordneten Klasse Sync platziert werden? Da diese Methode für faire und unfaire Sperren verwendet wird, basieren die tryLocks beider auf dieser Implementierung.
protected final boolean tryRelease(int releases)
Die Implementierung von tryRelease in AQS besteht darin, zunächst die Gesamtzahl der Sperren abzüglich der Anzahl der freigegebenen Sperren zu berechnen, um die Anzahl der verbleibenden gehaltenen Sperren c zu erhalten. Wenn c 0 ist, wird der Status auf den Wert von c gesetzt und zurückgegeben ob keine Sperren gehalten werden.
protected final boolean isHeldExclusively()
isHeldExclusively in AQS implementiert, return getExclusiveOwnerThread() == Thread.currentThread()
final int getHoldCount()
Gibt zurück, wie viele Sperren der aktuelle Thread hält. Diese Methode basiert auf der Beurteilung von isHeldExclusively
letzter boolescher Wert isLocked()
return getState() != 0; Ob ein Thread die Sperre hält
letzter Thread getOwner()
Gibt den Thread zurück, der die Sperre hält, oder null, wenn nicht
final ConditionObject newCondition()
newConditionObject()
private void readObject(java.io.ObjectInputStream s)
Deserialisierungsmethode, Status auf 0 setzen
statische Abschlussklasse FairSync
Faire Sperrimplementierungsklasse
erben
Synchronisieren
Hauptmethode
endgültige Void-Sperre()
erwerben(1)
Parameter 1 hat hier eine praktische Bedeutung, die sich von CountDownLatch unterscheidet.
protected final boolean tryAcquire(int acquires)
TryAcquire-Implementierung der fairen Sperre. Der Implementierungsprozess ist: Wenn die belegte Sperre c 0 ist, wenn sich kein Thread davor befindet und cas c erfolgreich auf „Akquise“ setzt, dann wird der aktuelle Thread als exklusiver Thread festgelegt und true zurückgegeben, wenn der aktuelle Thread bereits die Sperre hält. Aktualisieren Sie die Anzahl der Sperren und geben Sie „true“ zurück. Gibt in allen anderen Fällen „false“ zurück
hasQueuedPredecessors
Hier gibt es mehr Urteilsvermögen als eine unfaire Sperre. Auch wenn derzeit kein Thread die Sperre erhält, gibt er den Erwerb der Sperre auf, wenn ein Thread längere Zeit darauf wartet.
statische Endklasse NonfairSync
Unfaire Sperrimplementierungsklasse
erben
Synchronisieren
Hauptmethode
endgültige Void-Sperre()
Versuchen Sie es zuerst direkt mit cas(0,1). Wenn dies gelingt, bedeutet dies, dass derzeit niemand anderes die Sperre erhält. Legen Sie dann den exklusiven Thread als aktuellen Thread fest (1)
Tatsächlich versucht die tryAcquire-Methode der unfairen Sperre auch cas, um die Unfairness früher hervorzuheben.
protected final boolean tryAcquire(int acquires)
return nonfairTryAcquire(erwirbt) Die unfaire Implementierung existiert bereits in Sync, rufen Sie sie direkt auf
Mitgliedsvariablen
private endgültige Synchronisierung
Gibt an, ob die Sperre fair oder unfair implementiert ist und entspricht nach der Instanziierung FairSync oder NonfairSync.
Konstrukteur
public ReentrantLock()
Unfaire Sperre, die standardmäßig erstellt wurde
public ReentrantLock(boolean fair)
Konstruieren Sie je nach fairer Wahl ein faires Schloss oder ein unfaires Schloss
Hauptmethode
öffentliche void lock()
sync.lock()
public void lockInterruptably()
sync.acquireInterruptably(1)
öffentlicher boolescher tryLock()
return sync.nonfairTryAcquire(1); Wenn die Sperre erfolgreich erworben wurde oder der aktuelle Thread die Sperre bereits hält, wird true zurückgegeben, andernfalls wird false zurückgegeben
Egal um welches Schloss es sich hier handelt, es ist ein unfairer Versuch, an das Schloss zu gelangen.
public boolean tryLock(long timeout, TimeUnit-Einheit)
return sync.tryAcquireNanos(1, unit.toNanos(timeout))
tryAcquireNano ruft tryAcquire auf, daher gibt es einen Unterschied zwischen Fairness und Unfairness.
public void unlock()
sync.release(1)
öffentliche Bedingung newCondition()
sync.newCondition() zurückgeben
public int getHoldCount()
return sync.getHoldCount() Die Anzahl der vom aktuellen Thread gehaltenen Sperren
öffentlicher boolescher Wert isHeldByCurrentThread()
Geben Sie sync.isHeldExclusively() zurück, ob der aktuelle Thread die Sperre hält
öffentlicher boolescher Wert isLocked()
sync.isLocked() zurückgeben
öffentlicher finaler boolescher Wert isFair()
Gibt die Synchronisierungsinstanz von FairSync zurück
protected Thread getOwner()
sync.getOwner() zurückgeben
öffentlicher finaler boolescher Wert hasQueuedThreads()
Gibt sync.hasQueuedThreads() zurück, ob Threads in der Warteschlange vorhanden sind
public final boolean hasQueuedThread(Thread-Thread)
return sync.isQueued(thread); Gibt an, ob sich der aktuelle Thread in der Warteschlange befindet
public final int getQueueLength()
return sync.getQueueLength();
protected Collection<Thread> getQueuedThreads()
return sync.getQueuedThreads();
öffentlicher boolescher Wert hasWaiters(Bedingung Bedingung)
Ob Threads auf die gegebene Bedingung warten (entsprechend der Frage, ob Threads im Zustand CONDITION in der Bedingungswarteschlange vorhanden sind)
public int getWaitQueueLength(Bedingungsbedingung)
Die Anzahl der Threads im Zustand CONDITION in der Bedingungswarteschlange für eine bestimmte Bedingung
protected Collection<Thread> getWaitingThreads(Bedingung)
Zustand
Einführung
Schnittstelle; Bedingungsvariablen werden hauptsächlich verwendet, um die Abhängigkeit der Thread-Ausführung von bestimmten Zuständen zu verwalten, die Überwachungsmethoden des Objekts (wait/notify/notifyAll) auf direkte Objekte abzubilden und sie mit der Verwendung einer beliebigen Lock-Implementierung zu kombinieren, um jedes Objekt zu erreichen die Auswirkung mehrerer Wartesätze. Dies entspricht dem Ersetzen der Synchronisierung durch Lock und dem Ersetzen der Monitormethode durch Condition, d. h. Condition muss in Verbindung mit Lock verwendet werden.
Die Hauptunterschiede zwischen Bedingungs- und Überwachungsmethoden sind: 1. Bedingung unterstützt mehrere Warteschlangen und eine Sperrinstanz kann an mehrere Bedingungen gebunden werden. 2. Die Bedingung kann Antwortinterrupt- und Timeout-Einstellungen unterstützen
Die tiefe Bedeutung der Bindung mehrerer Bedingungen besteht darin, dass Threads je nach Situation verwaltet werden können. Die sogenannte „feinere“ Steuerung bedeutet, dass sich Threads, die aufgrund unterschiedlicher Bedingungen blockiert sind, in unterschiedlichen Bedingungswarteschlangen befinden und nur dann aktiviert werden, wenn sie aktiviert werden müssen . Wenn man in die Synchronisationswarteschlange einsteigt, unterscheidet und isoliert man sich
verstehen
Der Unterschied zum allgemeinen Sperrenwettbewerb besteht darin, dass Bedingungen in einer Umgebung mit mehreren Threads erfüllt sein müssen, bevor bestimmte Vorgänge ausgeführt werden können, während der allgemeine Sperrenwettbewerb lediglich dem Wettbewerb um das Recht zur Ausführung eines bestimmten Codeabschnitts entspricht .
Übliches Programmiermodell: Sperre erhalten; while (bedingter Zustand ist nicht erfüllt) { Freigabesperre; Der Thread bleibt hängen und wartet auf die Benachrichtigung, bis die Bedingung erfüllt ist. Erlange das Schloss erneut. } Kritische Abschnittsoperationen; Freigabesperre;
Ternäre Beziehung: Sperre, Warten, bedingte Behauptung (Prädikat). Jeder Warteaufruf ist implizit einer bestimmten bedingten Behauptung zugeordnet. Wenn der Aufrufer eine bestimmte bedingte Behauptung aufruft, muss er bereits die Sperre der bedingten Warteschlange halten, die sich auf die Behauptung bezieht, und diese Sperre muss die Bedingungen schützen, die die bedingte Behauptung bilden. Zustandsvariablen
Prädikat: eine Funktion, die den Typ Bool zurückgibt
erstellen
Die zu implementierende Klasse wird über die newCondition-Methode der Lock-Schnittstelle erstellt und implementiert ihre eigene Logik (im Wesentlichen erstellt sie eine Instanz von ConditionObject).
Hauptmethode
void wait()
Lassen Sie den Thread warten, bis er durch Signal oder Interrupt geweckt wird. Wenn er aufgerufen wird, wird die mit der Bedingung verbundene Sperre automatisch aufgehoben und der Thread stoppt die Ausführung der aktuellen Aufgabe, bis er durch Signal/signalAll/interrupt/spurious wakeup geweckt wird. Der Thread muss erneut versuchen, die mit der Bedingung verknüpfte Sperre zu erhalten, bevor er die Methode „await“ aufruft.
boolesches Warten (lange Zeit, TimeUnit-Einheit)
Ähnlich wie bei „awaitNanos“ können Sie die Zeiteinheit angeben und „false“ zurückgeben, wenn die Zeit abgelaufen ist, andernfalls „true“ zurückgeben
lange wartenNanos(long nanosTimeout)
Ähnlich wie wait, wacht jedoch spätestens nach der angegebenen Zeit auf und gibt ungenutzte Zeit zurück
void waitUninterruptably();
Ähnlich wie wait, reagiert jedoch nicht auf Interrupts
boolean waitingUntil(Datumsfrist)
Ähnlich wie bei „awaitNanos“ ist die Art und Weise, die Zeit anzugeben, anders. Wenn die Frist erreicht ist, wird „false“ zurückgegeben, andernfalls wird „true“ zurückgegeben.
Void-Signal()
Wecken Sie einen Thread im Wartezustand auf. Nachdem der Thread aufgewacht ist, muss er die Sperre erneut erlangen.
void signalAll()
Wecken Sie alle wartenden Threads auf
Sperren
Einführung
Die Schnittstelle bietet einige gängige Methoden zum Anfordern/Freigeben von Sperren
Vergleich mit synchronisiert
Die zugehörigen synchronisierten Mechanismen (z. B. Überwachungsobjekte usw.) sind integriert und können nicht auf Sprachebene aufgerufen werden. Lock ist eine alternative Lösung auf Sprachebene, sodass Sie einige Annehmlichkeiten auf Sprachebene erhalten können (z. B. Wissen). ob die Sperre erfolgreich erworben wurde usw.)
Der Sperr- und Entsperrvorgang von synchronisiert wird durch das Schlüsselwort selbst abgeschlossen, während Lock die Sperr- und Entsperrmethode explizit aufrufen muss.
Lock kann Bedingungsvariablen verwenden, um Sperren flexibler zu nutzen
Lock kann auf Interrupts reagieren, synchronisiert jedoch nicht
Hauptmethode
Leere Sperre()
void lockInterruptably()
Auf Unterbrechungen wird während des Sperranforderungsprozesses reagiert.
boolean tryLock()
Versuchen Sie, die Sperre zu erhalten und das Ergebnis direkt zurückzugeben (ohne den aktuellen Thread zu blockieren).
boolean tryLock(long time, TimeUnit-Einheit)
tryLock mit Timeout und Antwortinterrupt
void unlock();
Bedingung newCondition()
Gibt ein mit der Sperre verknüpftes Condition-Objekt zurück
LockSupport
Einführung
Grundlegende Thread-Blockierungsprimitive (im Zusammenhang mit Berechtigungen), die zum Erstellen von Sperren und anderen Synchronisierungsklassen verwendet werden
Die unterste Ebene basiert auf einer unsicheren Implementierung
Konstruktionsmethode
Der private Bau von Objekten dieser Klasse ist nicht gestattet
Mitgliedsvariablen
private static final sun.misc.Unsafe UNSICHER
Bauen Sie einen statischen Codeblock ein
sun.misc.Unsafe
privater statischer finaler langer parkBlockerOffset
Ermitteln Sie die Versatzposition des parkBlocker-Attributs im Thread-Klassenobjekt über Unsafe
parkBlocker ist ein Datensatz, wenn der Thread blockiert ist. Dies erleichtert Überwachungs- und Diagnosetools, um den Grund für die Blockierung des Threads zu ermitteln. Wenn der Thread nicht blockiert ist, ist er null.
privates statisches finales langes SEED
Ermitteln Sie die Offset-Position der threadLocalRandomSeed-Eigenschaft in der Thread-Klasse über Unsafe
privater statischer finaler langer PROBE
Ermitteln Sie die Offset-Position der threadLocalRandomProbe-Eigenschaft in der Thread-Klasse über Unsafe
privates statisches Finale lang SEKUNDÄR
Ermitteln Sie die Offset-Position der threadLocalRandomSecondarySeed-Eigenschaft in der Thread-Klasse über Unsafe
Diese drei Eigenschaften werden von ThreadLocalRandom festgelegt
Hauptmethode
öffentliches statisches Objekt getBlocker(Thread t)
Rufen Sie das parkBlocker-Objekt über Unsafe im Thread-Objekt ab
private static void setBlocker(Thread t, Object arg)
Private Methode, legen Sie das parkBlocker-Objekt im Thread über Unsafe fest. Die folgenden Methoden zum Festlegen von Blockern rufen diese Methode auf
öffentlicher statischer leerer Park()
Blockieren Sie den Thread und warten Sie auf „Zulassen“, um die Ausführung fortzusetzen.
Spezifische Implementierung (Hotspot): Parker-Klasse
具体的实现与平台相关,每个线程都有一个Parker实例,在linux实现中实际上是以mutex和condition最终实现了锁和阻塞的过程(具体看笔记文章,这里不写了)
主要成员变量(linux实现)
private volatile int _counter
表示许可,大于0表示已获取许可,每次park只会将其设置为1,不会递增
Wenn Sie daher zweimal „unpark“ und dann zweimal „park“ aufrufen, wird es beim zweiten Mal immer noch blockiert.
protected pthread_mutex_t _mutex[1]
互斥变量
在代码中可以通过linux原子指令pthread_mutex_lock、pthread_mutex_trylock、pthread_mutex_unlock对变量进行加锁和解锁操作
protected pthread_cond_t _cond[1]
条件变量
利用线程间共享的全局变量进行同步的一种机制,一般和互斥锁结合使用(避免条件变量的竞争);可以通过pthread_cond_wait、pthread_cond_timedwait等指令进行操作
Außerdem gibt es im Linux-Kernel eine Warteschlange zur Verwaltung blockierter Prozesse
方法
park
unpark
Unterschiede in der Verwendung von warten
Warten muss auf eine Benachrichtigung warten, bevor es aktiviert werden kann. Es gibt einen sequentiellen Prozess, und das Programm, auf das der Park wartet, kann vor dem Parken aufgerufen werden.
„wait“ muss den Monitor des Objekts abrufen, „park“ jedoch nicht
Beachten
Wenn im Park eine Unterbrechung auftritt, kehrt Park ebenfalls zurück, löst jedoch keine Unterbrechungsausnahme aus.
öffentlicher statischer leerer Park (Objektblocker)
Aufgrund der Rolle von parkBlocker wird eher die Verwendung der Park-Methode mit Blocker-Parametern empfohlen. Nach der Verwendung können Sie über Diagnosetools wie jstack sehen, auf welches Objekt der Thread blockiert ist, z. B. auf das Parken, auf das gewartet werden soll <0x000000045ed12298> (ein vollständig qualifizierter Name eines xxxx-Objekts)
öffentlicher statischer VoidparkNanos (Objektblocker, lange Nanos)
öffentlicher statischer HohlraumparkNanos (lange Nanos)
Die maximale Blockierungszeit, bevor das Programm mit der Ausführung fortfährt
öffentlicher statischer leerer ParkUntil(Objektblocker, lange Frist)
öffentlicher statischer leerer ParkBis (lange Frist)
public static void unpark (Thread-Thread)