Im C-Programmcode können wir die vom Betriebssystem bereitgestellte Mutex-Sperre verwenden, um den MUTEX-Zugriff auf Synchronblöcke und Fadenblockierungen und Weckarbeiten zu erreichen. In Java werden jedoch neben der Bereitstellung von Lockapi auch synchronisierte Schlüsselwörter auf Syntaxebene bereitgestellt, um die Primitive der Mutex -Synchronisation zu implementieren. Wie können Sie den synchronisierten Schlüssel in der JVM implementieren?
1. Synchronisierte Bytecode -Darstellung:
In der Java-Sprache gibt es zwei integrierte synchronisierte Syntaxe: 1. Synchronisierte Aussagen; 2. Synchronisierte Methode. Für synchronisierte Anweisungen, wenn der Java -Quellcode von Javac in Bytecode kompiliert wird, werden die Anweisungen zum Monitorexit -Bytecode jeweils an den Eingangs- und Ausgangspositionen des Synchronisationsblocks eingefügt. Die synchronisierte Methode wird in ordentliche Methodenanweisungen übersetzt und zurückgibt, wie z. Auf der VM -Bytecode -Ebene gibt es keine speziellen Anweisungen zur Implementierung der synchronisierten modifizierten Methode. Stattdessen wird die synchronisierte Flag -Position 1 im Feld "Access_flags" der Methode in der Methodentabelle der Klassendatei platziert, die angibt, dass die Methode eine synchronisierte Methode ist und das Objekt verwendet, das die Methode oder die Klassen, die zur Methode gehören, um Klass als Sperrobjekt darzustellen.
2. Optimierung von Schlössern in JVM:
Einfach ausgedrückt, Monitorexit -Bytecode in JVM verlassen sich auf Mutexlock des zugrunde liegenden Betriebssystems, um es zu implementieren. Da die Verwendung von Mutexlock jedoch das Aufsetzen des aktuellen Threads und das Umschalten von Benutzerstatus zu Kernel -Status erfordert, ist diese Umstellung sehr teuer. In den meisten Fällen in der Realität wird die Synchronisationsmethode jedoch in einer Eingebung mit einem Threaden (lockless Wettbewerbsumgebung) ausgeführt. Wenn jedes Mal Mutexlock aufgerufen wird, wird dies die Leistung des Programms ernsthaft beeinflussen. In JDK1.6 wurden jedoch viele Optimierungen für die Implementierung von Schlössern wie Vergröberung, Sperreliminierung, leichte Verriegelung, vorgespannte Verriegelung, adaptives Spinning und andere Technologien eingeführt, um den Overhead des Sperrbetriebs zu verringern.
LockCoarsening: Das heißt, reduzieren unnötige Sperr- und Sperrvorgänge und erweitern Sie mehrere kontinuierliche Schlösser mit einem größeren Bereich.
Sperrausscheidung: Durch die Fluchtanalyse durch den JIT -Compiler der Laufzeit wird ein gewisser Schlossschutz beseitigt. Einige Daten, die nicht von anderen Threads außerhalb des aktuellen Synchronisationsblocks geteilt werden. Durch die Escape -Analyse kann der Objektraum auf dem lokalen Thread -Stapel zugewiesen werden (gleichzeitig kann er auch den Müllsammlungsaufwand auf dem Haufen reduzieren).
Leichtweightlocking: Die Implementierung dieses Schlosses basiert auf der Annahme, dass in realen Fällen der größte Teil des Synchronisierungscode in unserem Programm im Allgemeinen in einem lock-freien Wettbewerbszustand (d. H. Eine Einzelausführungsumgebung mit einem Thread) liegt. Im Falle eines lock-freien Wettbewerbs kann es vollständig vermeiden, dass das Aufrufen von Mutexes im Betriebssystem auf dem Vorgang des Betriebssystems vollständig eingerichtet wird. Stattdessen müssen Sie sich in Monitor und Monitorexit nur auf eine CAS -Atomanweisung verlassen, um die Erfassung und Freisetzung des Schlosses abzuschließen. Wenn es einen Schlosswettbewerb gibt, ruft der Thread, der CAS -Anweisungen nicht ausführen kann, das Betriebssystem -Mutex auf, um den Blockierungszustand einzugeben und aufzuwachen, wenn die Sperre veröffentlicht wird (die spezifischen Verarbeitungsschritte werden im Folgenden im Detail erörtert).
BIASEDLOCKING: Es soll nicht unnötige CAS-Atomanweisungen während des Schlosseserwerbs im Falle eines lock-freien Wettbewerbs ausführen, da die CAS-Atomanweisungen zwar im Vergleich zu Sperrungen im Schwergewicht relativ gering sind, aber immer noch sehr beträchtliche lokale Verzögerungen haben (siehe Artikel).
Adaptive Spinning: Wenn ein Thread während des Erwerbs eines leichten Schlosses nicht den CAS -Betrieb durchführt, wird ein geschäftiges Warten eingetragen, bevor Sie das mit dem Monitor zugeordnete Schwergewichtsschloss (MutexSemaphor) eingeben und dann erneut versuchen. Wenn es nach einer bestimmten Anzahl von Versuchen immer noch fehlschlägt, wird das mit dem Monitor (d. H., Mutex Lock) assoziierte Semaphor aufgerufen, um den Blockierungszustand einzugeben.
3. Jectheader:
Beim Erstellen eines Objekts in der JVM werden zwei Word-Größe-Objekt-Header vor dem Objekt hinzugefügt. Ein Wort auf der 32-Bit-Maschine ist 32bit. Unterschiedliche Inhalte werden in Markworld gemäß unterschiedlichen Statusbits gespeichert. Wie in der obigen Abbildung gezeigt, ist Markword in einem leichten Schloss in zwei Teile unterteilt. Zu Beginn wird Lockwort auf HashCode eingestellt, und die niedrigsten drei Bits repräsentieren den Zustand, in dem sich das Lockword befindet. Der Anfangszustand ist 001 repräsentiert den lockfreien Zustand. KLASPTR zeigt auf die Adresse, die durch das Objekt dargestellt wird, dessen Klassen -Bytecode in der virtuellen Maschine liegt. Felder repräsentieren kontinuierliche Objektinstanzfelder.
4. Monitorrecord:
Monitorrecord ist eine private Datenstruktur von Threads. Jeder Thread verfügt über eine Liste der verfügbaren Monitorrecords und eine globale verfügbare Liste. Wie nutzen diese Monitorrecords? Jedes gesperrte Objekt wird einem Monitorrecord verknüpft (Sperrenwort in den Objekt -Headerpunkten auf die Startadresse von Monitorrecord. Da diese Adresse 8Byte ausgerichtet ist, können die niedrigsten drei Bits des Schlossworts als Statusbits verwendet werden). Gleichzeitig gibt es in Monitorrecord ein Eigentümerfeld, um die eindeutige Kennung des Fadens zu speichern, der das Schloss besitzt, und zeigt an, dass das Schloss von diesem Thread besetzt ist. Die folgende Abbildung zeigt die interne Struktur von Monitorrecord:
Besitzer: Null am Anfang bedeutet, dass derzeit kein Thread den Monitor -Datensatz besitzt. Wenn der Thread das Schloss erfolgreich besitzt, speichert er die eindeutige Identität des Threads, und wenn das Schloss freigegeben wird, wird er auf null gesetzt.
Eintrag: Assoziieren Sie einen System Mutex (Semaphor), um alle Threads zu blockieren, die den Monitor -Datensatz nicht sperren.
RCTHIS: Repräsentiert die Anzahl der blockierten Threads oder wartet auf den Monitor -Datensatz.
NEST: Wird verwendet, um die Zählung von Wiedereintrittsschlössern zu implementieren.
HashCode: Speichert den HashCode -Wert, der aus dem Objektkopf kopiert wird (kann auch GC -Alter enthalten).
Kandidat: Wird verwendet, um unnötiges Blockieren zu vermeiden oder darauf zu warten, dass Threads aufwachen, da nur ein Thread das Schloss jedes Mal erfolgreich besitzen kann. Wenn der vorherige Thread, der das Schloss veröffentlichen, jedes Mal alle blockierenden oder wartenden Fäden aufweckt, führt dies zu unnötigem Kontextschalter (von der Blockierung bis zur Bereitschaft und dann aufgrund des Versagens des Wettbewerbs) und somit zu einer starken Leistungsverschlechterung führt. Der Kandidat hat nur zwei mögliche Werte: 0 bedeutet, dass es keinen Thread gibt, der bis zu 1 geweckt werden muss, um einen Nachfolge -Thread aufzuwachen, um um das Schloss zu konkurrieren.
5. Spezifische Implementierung von leichten Schlössern:
Ein Thread kann ein Objekt auf zwei Arten sperren: 1. Erhalten Sie die Sperre des Objekts, indem Sie ein Objekt in einem lock-freien Zustand erweitern (Statusbit 001); 2. Das Objekt befindet sich bereits in einem erweiterten Status (Statusbit 00), aber das Feld des Eigentümers des Monitor -Datensatzes, auf den Lockword verweist, ist null, sodass Sie direkt versuchen können, den Eigentümer über die CAS -Atomanweisung auf seine eigene Identität zu setzen, um die Sperre zu erhalten.
Der allgemeine Prozess des Erhaltens eines Schlosses (Monitorenter) lautet wie folgt:
(1) Wenn sich das Objekt in einem lock-freien Zustand befindet (der Rekordword-Wert ist HashCode, beträgt das Statusbit 001), erhält der Thread zunächst einen kostenlosen Monitor-Datensatz in seiner verfügbaren Monitor-Datensatzliste. Die anfänglichen Nest- und Eigentümerwerte sind auf 1 und die eigene Identifizierung des Threads voreingestellt. Sobald der Monitor -Datensatz fertig ist, installieren wir die Startadresse des Monitor -Datensatzes an das Sperrenfeldfeld des Objektkopfs durch die Cas Atomic -Anweisung, um zu erweitern (der ursprüngliche Text ist aufblasen. Der Grund, warum er als Aufblasen bezeichnet wird, liegt hauptsächlich daran, dass das Objekt erweitert wird, nachdem es erweitert wird. Aus Erweiterung des Spatialseffizienzes wird das Aufnehmen von dem Objekt anmeldet. Das Objekt wird zum Aufnehmen von dem Objekt ausgezeichnet. Wenn das Objekt ein bis zum Objekt angehängt wird, wird das Objekt. Das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Durch das Objekt. Wenn Sie das Objekt ein-. Dieses Papier.
(2) Das Objekt wurde erweitert und der im Eigentümer gespeicherte Thread wird als der Thread identifiziert, der das Schloss selbst erwirbt. Dies ist der Fall von Wiedereintrittsschlössern. Sie müssen nur 1 zum Nest hinzufügen. Es ist kein Atombetrieb erforderlich und sehr effizient.
(3) Das Objekt wurde erweitert, aber der Eigentümerwert ist null. Dieser Zustand tritt auf, wenn ein Faden, der gleichzeitig blockiert oder auf ein Schloss wartet, gesperrt ist. Der Vorbesitzer des Schlosses hat gerade das Schloss veröffentlicht. Zu diesem Zeitpunkt versuchen mehrere Threads, den Eigentümer durch die Cas Atomic-Anweisung auf seine eigene Identität zu setzen, um die Sperre in einem Wettbewerbszustand mit mehreren Threaden zu erhalten. Der Thread, der nicht konkurriert, wird in den Ausführungspfad des vierten Falls (4) eingegeben.
(4) Das Objekt befindet sich in einem erweiterten Zustand und der Eigentümer ist nicht null (gesperrt). Es dreht sich eine bestimmte Anzahl von Malen, bevor es den Schwergewichts -Mutex des Betriebssystems aufruft. Wenn eine bestimmte Anzahl von Male erreicht ist, ist es Zeit, in den Blockierungszustand einzusteigen, wenn das Schloss noch nicht erfolgreich erhalten wird. Fügen Sie zunächst den Wert von rfthis atomisch um 1 hinzu. Da andere Threads die Beziehung zwischen dem Objekt und der Überwachung der Datensatz während der Zugabe von 1 zerstören können, ist es erforderlich, einen weiteren Vergleich nach dem Hinzufügen von 1 durchzuführen, um sicherzustellen, dass der Wert des Sperrworts nicht geändert wurde. Wenn festgestellt wird, dass es geändert wurde, muss der Monitorenter -Prozess wiederholt werden. Gleichzeitig wird erneut beobachtet, ob der Besitzer null ist. In diesem Fall werden CAS angerufen, um am Wettbewerbsschloss teilzunehmen. Wenn der Lock -Wettbewerb fehlschlägt, wird er in einen Blockierungszustand eintreten.
Der allgemeine Prozess der Veröffentlichung des Schlosses (Monitorexit) lautet wie folgt:
(1) Überprüfen Sie zunächst, ob sich das Objekt in einem erweiterten Zustand befindet und der Thread der Eigentümer des Schlosses ist. Wenn festgestellt wird, dass es falsch ist, wird eine Ausnahme ausgelöst.
(2) Überprüfen Sie, ob das Nestfeld größer als 1 ist. Wenn es größer als 1 ist, reduzieren Sie einfach das Nest um 1 und haben weiterhin das Schloss. Wenn es gleich 1 ist, geben Sie Schritt (3) ein.
(3) Überprüfen Sie, ob rfthis größer als 0 ist, setzen Sie den Besitzer auf Null und wecken Sie einen Blockier- oder Wartenfaden, um das Schloss erneut zu erwerben. Wenn es gleich 0 ist, wird Schritt (4) eingegeben.
(4) Entlösen Sie ein Objekt, geben Sie die Sperre frei, indem Sie das Sperre des Objekts an den ursprünglichen HashCode -Wert ersetzen, um die Sperre zu veröffentlichen, und geben Sie den Monitor -Datensatz wieder an den Thread.
Zusammenfassen
Referenz: " Tiefes Verständnis der erweiterten Funktionen und Best Practices der Java Virtual Machine JVM (Zhou Zhiming) "
Das obige ist der gesamte Inhalt dieses Artikels über synchronisierte und Implementierungsproblemanalyse von JVM -Details. Ich hoffe, es wird für alle hilfreich sein. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen. Vielen Dank an Freunde für Ihre Unterstützung für diese Seite!