1. Schwergewichtsschloss
Im vorherigen Artikel haben wir die Verwendung von synchronisierten und der Umsetzungsprinzipien eingeführt. Jetzt sollten wir wissen, dass synchronisiert über ein Monitor -Sperre im Objekt implementiert wird. Das Monitor -Sperre wird jedoch im Wesentlichen implementiert, indem sich auf das Mutex -Sperre des zugrunde liegenden Betriebssystems stützt. Das Betriebssystem muss zwischen den Threads vom Benutzerzustand zu einem Kernstatus wechseln. Dies ist sehr kostspielig und die Umwandlung zwischen Staaten dauert relativ lange. Aus diesem Grund ist synchronisiert ineffizient. Daher nennen wir diese Sperre, die auf der Implementierung des Betriebssystems Mutex Lock "A Heavyweight Lock" beruht. Der Kern der verschiedenen Optimierungen, die in JDK synchronisiert wurden, besteht darin, die Verwendung dieses Schwergewichtsschloss zu verringern. Nach JDK1.6, um den Leistungsverbrauch zu verringern, der durch die Erlangung und Freisetzung von Schlössern und die Verbesserung der Leistung verursacht wurde, wurden "leichte Schlösser" und "voreingenommene Schlösser" eingeführt.
2. Leichtes Schloss
Es gibt vier Arten von Schlosszuständen: lockfreier Zustand, vorgespannter Schloss, leichtes Schloss und Schwergewichtsschloss. Mit der Konkurrenz von Schlössern können Schlösser von voreingenommenen Schlössern auf leichte Schlösser verbessert und dann Schwergewichtsschlösser verbessert werden (aber das Upgrade von Schlössern ist ein Weg, was bedeutet, dass sie nur von niedrig auf hoch auf das Upgrade von Locks aufrüsten können). In JDK 1.6 sind die Vorspannungsschloss und das leichte Schloss standardmäßig aktiviert. Wir können auch die Vorspannungssperrung durch -xx: -UnedebiaSedlocking deaktivieren. Der Sperrstatus wird in der Header-Datei des Objekts gespeichert und nimmt den 32-Bit-JDK als Beispiel:
Sperrstatus | 25 Bit | 4bit | 1bit | 2bit | ||
23bit | 2bit | Ist es voreingenommenes Schloss? | Sperre Flag -Bit | |||
Leichtes Schloss | Zeiger, um Datensätze im Stapel zu sperren | 00 | ||||
Schwergewichtsschloss | Zeiger auf Mutex (Schwergewichtsschloss) | 10 | ||||
GC -Tags | NULL | 11 | ||||
Positive Schloss | Thread -ID | Epoche | Die Probanden sind von altersüchtig | 1 | 01 | |
Locklos | Hashcode des Objekts | Die Probanden sind von altersüchtig | 0 | 01 | ||
"Leichtes Gewicht" ist relativ zu herkömmlichen Schlössern, die das Betriebssystem -Mutexes verwenden. Es ist jedoch wichtig zu betonen, dass leichte Schlösser nicht zum Ersetzen von Schwergewichtsschlössern verwendet werden. Seine ursprüngliche Absicht ist es, den Leistungsverbrauch zu verringern, der durch die Verwendung herkömmlicher Schwergewichtsschlösser ohne Multi-Thread-Wettbewerb erzeugt wird. Bevor wir den Ausführungsprozess von leichten Schlössern erläutern, verstehen wir zunächst, dass die an leichten Schlösser angepassten Szenarien der Fall sind, in dem Threads synchrone Blöcke ausführen. Wenn gleichzeitig auf das gleiche Schloss zugegriffen wird, wird das leichte Schloss in ein Schwergewichtsschloss erweitert.
1. Der Verriegelungsprozess des leichten Schloss
(1) Wenn der Code in den Synchronisationsblock eingeht, ist der Sperrstatus des Synchronisationsobjekts lock-frei (das Sperrflag ist "01" -Zustand, ob es sich um eine voreingenommene Sperre handelt, die virtuelle Maschine erstellt zuerst einen Speicherplatz, der als Sperrdatensatz bezeichnet wird, um die aktuelle Kopie des aktuellen Marks des Sperrobjekts zu speichern, das offiziell als entlastete Marke bezeichnet wird. Zu diesem Zeitpunkt ist der Zustand des Fadenstapels und des Objektkopfs in Abbildung 2.1 dargestellt.
(2) Kopieren Sie das Markwort im Objektkopf und kopieren Sie es in den Sperrdatensatz.
(3) Nachdem die Kopie erfolgreich ist, verwendet die virtuelle Maschine CAS -Operationen, um das Mark -Wort des Objekts an einen Zeiger zu aktualisieren, um den Datensatz zu sperren, und den Eigentümerzeiger in den Sperrdatensatz auf Objektmarkierungswort zu verweisen. Wenn das Update erfolgreich ist, führen Sie Schritt (3) aus, andernfalls führen Sie Schritt (4) aus.
(4) Wenn diese Aktualisierungsaktion erfolgreich ist, hat der Thread die Sperre des Objekts und das Sperrflag des Objektmarksworts auf "00", was bedeutet, dass sich das Objekt in einem leichten Zustand befindet. Zu diesem Zeitpunkt ist der Zustand des Fadenstapels und der Objektkopf in Abbildung 2.2 dargestellt.
(5) Wenn diese Aktualisierungsoperation fehlschlägt, prüft die virtuelle Maschine zunächst, ob das Mark -Wort des Objekts auf den Stapelrahmen des aktuellen Threads zeigt. In diesem Fall bedeutet dies, dass der aktuelle Thread bereits die Sperre des Objekts hat und dann direkt den Synchronisationsblock eingeben kann, um die Ausführung fortzusetzen. Andernfalls konkurrieren mehrere Fäden um Schlösser, und das leichte Schloss erweitert sich in ein Schwergewichtsschloss, und der Zustandswert der Sperrflagge wird zu "10". Der Zeiger auf das Schwergewichtsschloss (Mutex) wird in Mark -Word gespeichert, und der Faden, der auf das Schloss wartet, gelangt auch in den Blockierungszustand. Der aktuelle Thread versucht, das Schloss zu erwerben. Der Spin besteht darin, das Blockieren des Fadens zu vermeiden und eine Schleife zu verwenden, um das Schloss zu erwerben.
Abbildung 2.1 Der Status von Stapel und Objekt vor dem CAS -Betrieb der Leichtgewichtsschloss
Abbildung 2.2 Der Status von Stapel und Objekt nach leichter Lock CAS -Operation
2. Entsperrungsprozess des leichten Schlosses:
(1) Versuchen Sie, das Word -Objekt für verdrückte Mark durch CAS -Operation zu ersetzen.
(2) Wenn der Ersatz erfolgreich ist, wird der gesamte Synchronisationsprozess abgeschlossen.
(3) Wenn der Ersatz fehlschlägt, bedeutet dies, dass andere Threads versucht haben, das Schloss zu erwerben (das Schloss wurde zu diesem Zeitpunkt erweitert), und dann muss der suspendierte Faden geweckt werden, während das Schloss veröffentlicht wird.
3. Positives Schloss
Die Einführung von Bias-Sperre besteht darin, unnötige leichte Ausführungspfade ohne Multi-Thread-Konkurrenz zu minimieren, da die Erfassung und Freisetzung von leichten Schlössern von mehreren CAS-Atomanweisungen abhängt, während Bias-Schlösser nur auf einen CAS-Atomanweisungen angewiesen werden müssen. Atomanweisungen). Wie oben erwähnt, werden leichte Schlösser verwendet, um die Leistung zu verbessern, wenn Threads abwechselnd synchrone Blöcke ausführen, während vorbisige Sperren verwendet werden, um die Leistung weiter zu verbessern, wenn nur ein Thread synchrone Blöcke ausführt.
1. Der vorgespannte Verriegelungsakquisitionsprozess:
(1) Zugriff, ob die Flagge der Vorspannungssperrung in Markwort auf 1 gesetzt ist und ob das Sperrflag 01 ist - bestätigen Sie, dass es sich um einen voreingenommenen Zustand handelt.
(2) Wenn es sich um einen voreingenommenen Zustand handelt, testen Sie, ob die Thread -ID auf den aktuellen Thread zeigt. Wenn dies der Fall ist, geben Sie Schritt (5) ein, andernfalls geben Sie Schritt (3) ein.
(3) Wenn die Thread -ID nicht auf den aktuellen Thread verweist, wird die Sperre durch den CAS -Betrieb konkurriert. Wenn der Wettbewerb erfolgreich ist, legen Sie die Thread -ID in Mark -Word auf die aktuelle Thread -ID und führen Sie aus (5). Wenn der Wettbewerb fehlschlägt, führen Sie (4) aus.
(4) Wenn CAS keine Vorspannungsschloss erfasst, bedeutet dies, dass es einen Wettbewerb gibt. Wenn der globale SafePoint erreicht ist, wird der Gewinde, der das Vorspannungsschloss erhält, suspendiert. Das Vorspannungsschloss wird auf ein leichtes Schloss aktualisiert, und der am SafePoint blockierte Faden führt den Synchronisationscode weiter aus.
(5) Führen Sie den Synchronisationscode aus.
2. Freisetzung von voreingenommenem Schloss:
Der Widerruf der voreingenommenen Schloss wird im vierten Schritt oben erwähnt. Das vorgespannte Schloss löst das Schloss nur frei, wenn andere Threads versuchen, um das vorgespannte Schloss zu konkurrieren, und der Thread wird das vorgespannte Schloss nicht aktiv freigeben. Die Stornierung des voreingenommenen Schlosses erfordert das Warten auf den globalen Sicherheitspunkt (zu diesem Zeitpunkt wird kein Bytecode ausgeführt). Es wird zuerst den Faden mit dem vorgespannten Schloss pausieren, feststellen, ob sich das Sperrobjekt in einem verschlossenen Zustand befindet, und dann zum entsperrten (Flag -Bit "01") oder leichtes Schloss (Flag -Bit ist "00") nach dem Rückgängigmachen des vorgespannten Schlosses zurück.
3.. Umwandlung zwischen Schwergewichtsschloss, Leichtgewicht und Vorspannungsschloss
Abbildung 2.3 Das Umrechnungsdiagramm der drei
Dieses Bild ist hauptsächlich eine Zusammenfassung des obigen Inhalts. Wenn Sie ein gutes Verständnis des obigen Inhalts haben, sollte das Bild leicht zu verstehen sein.
4. Andere Optimierungen
1. Adaptive Spinning: Aus dem Prozess des Erhaltens leichter Schlösser wissen wir, dass ein Gewinde beim Erwerb von leichten Schlössern nicht den CAS -Betrieb durchführt, es notwendig ist, das Schwergewichtsschloss durch Spin zu erhalten. Das Problem ist, dass Spin CPU -Verbrauch erfordert. Wenn das Schloss nicht erhalten werden kann, befindet sich der Faden in einem Spinzustand und Verschwendung von CPU -Ressourcen vergeblich. Der einfachste Weg, dieses Problem zu lösen, besteht darin, die Anzahl der Spins anzugeben, beispielsweise 10 Mal zyklieren und einen Blockierungszustand eingeben, wenn das Schloss nicht erhalten wird. Aber JDK verwendet einen intelligenteren Ansatz - adaptiver Spin. Einfach ausgedrückt, wenn der Thread erfolgreich ist, wird die Anzahl der Drehungen das nächste Mal mehr sein, und wenn der Spin fehlschlägt, wird die Anzahl der Spins reduziert.
2. Verkleinerung: Das Konzept der Verriegelungskosten sollte einfacher zu verstehen sein, nämlich das Zusammenführen der Sperre und das Entsperren von Operationen, die mehrmals miteinander verbunden sind und mehrere kontinuierliche Schlösser mit einem größeren Bereich erweitern. Zum Beispiel:
paket com.paddx.test.string; public class stringBufferest {stringBuffer stringBuffer = new StringBuffer (); public void append () {StringBuffer.Append ("a"); StringBuffer.Append ("B"); StringBuffer.Append ("C"); }}Hier ist jedes Mal, wenn Sie die StringBuffer.Append -Methode anrufen, verkleinert und entsperrt. Wenn die virtuelle Maschine eine Reihe von Sperren und Entsperren von Vorgängen auf demselben Objekt erkennt, wird sie in eine größere Reihe von Sperren und Entsperrvorgängen zusammengeführt, dh das Sperren wird nach Abschluss der letzten Anhangsmethode durchgeführt.
3.. Ausscheidung der Schloss: Die Beseitigung von Schloss bedeutet, unnötige Sperrvorgänge zu entfernen. Laut der Code Escape-Technologie wird festgestellt, dass ein Code-Stück und die Daten auf dem Haufen nicht aus dem aktuellen Thread entkommen, dann kann berücksichtigt werden, dass dieses Stück Code Thread-Safe ist und es nicht erforderlich ist, ihn zu sperren. Schauen Sie sich das folgende Programm an:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedTest02 {public static void main (String [] args) {synchronisierte Test02 test02 = new Synchronizedtest02 (); // Starten Sie das Aufwärmen für (int i = 0; i <10000; i ++) {i ++; } long start = system.currentTimemillis (); für (int i = 0; i <100000000; i ++) {Test02.Append ("ABC", "def"); } System.out.println ("time =" + (System.currentTimemillis () - Start)); } public void append (string str1, string str2) {stringBuffer sb = new StringBuffer (); sb.Append (str1) .Append (str2); }}Obwohl der Anhang von StringBuffer eine synchrone Methode ist, gehört der StringBuffer in diesem Programm zu einer lokalen Variablen und entzieht sich nicht der Methode. Daher ist dieser Vorgang tatsächlich mit Thread-sicher und kann das Schloss beseitigen. Hier ist das Ergebnis meiner lokalen Ausführung:
Um die Auswirkungen anderer Faktoren zu minimieren, ist die Vorspannung (-xx: useBiaSedLocking) hier deaktiviert. Im obigen Programm ist ersichtlich, dass die Leistung nach der Beseitigung des Schlosses erheblich verbessert wurde.
Hinweis: Die Ausführungsergebnisse können zwischen verschiedenen Versionen von JDK unterschiedlich sein. Die JDK -Version, die ich hier verwende, ist 1.6.
5. Zusammenfassung
Dieser Artikel konzentriert sich auf die Optimierung von synchronisierten in JDK, z. B. leichten Schlössern und voreingenommenen Schlössern, diese beiden Schlösser sind jedoch nicht vollständig ohne Mängel. Wenn der Wettbewerb beispielsweise heftig ist, verbessert er nicht nur die Effizienz, sondern verringert die Effizienz, da ein zusätzlicher Aufrüstungsprozess vorhanden ist. Zu diesem Zeitpunkt ist es notwendig, voreingenommene Schlösser durch -xx: usebiasedlocking zu deaktivieren. Hier ist ein Vergleich dieser Schlösser:
Sperren | Vorteil | Mangel | Anwendbare Szenarien |
Positive Schloss | Locking und Entsperrung erfordert keinen zusätzlichen Verbrauch, und es gibt eine Nanosekundenlücke im Vergleich zur Durchführung einer asynchronen Methode. | Wenn zwischen den Threads eine Schlosswettbewerbswettbewerb besteht, wird dies zu einem zusätzlichen Verbrauch des Widerrufs von Sperrung führen. | Geeignet für Szenarien, in denen nur ein Faden auf den synchronen Block zugreift. |
Leichtes Schloss | Konkurrierende Threads blockieren nicht, was die Reaktionsgeschwindigkeit des Programms verbessert. | Wenn der Thread, der nie den Schlosswettbewerb erhält, die CPU verbraucht. | Reaktionszeit verfolgen. Die Synchronblockausführung ist sehr schnell. |
Schwergewichtsschloss | Der Thread -Wettbewerb verwendet keinen Spin und verbraucht keine CPU. | Fadenblockierung, langsame Reaktionszeit. | Durchsatz verfolgen. Die Synchronisationsblock -Ausführungsgeschwindigkeit ist relativ lang. |