Heim  >  Artikel  >  Java  >  So optimieren Sie Java-Sperren

So optimieren Sie Java-Sperren

PHPz
PHPznach vorne
2023-05-16 08:16:051298Durchsuche

Sperroptimierung

Die Sperroptimierung bezieht sich hier hauptsächlich auf die Optimierung der Synchronisierung durch JVM.

Spin-Lock

Die Mutex-Synchronisation in den Blockierungszustand ist sehr teuer und sollte so weit wie möglich vermieden werden. In vielen Anwendungen werden freigegebene Daten nur für kurze Zeit gesperrt. Die Idee einer Spin-Sperre besteht darin, einem Thread zu ermöglichen, für einen bestimmten Zeitraum eine Besetztschleife (Spin) auszuführen, wenn eine gemeinsame Datensperre angefordert wird. Wenn die Sperre während dieses Zeitraums erhalten werden kann, kann der Eintritt in den Blockierungszustand vermieden werden .

Obwohl die Spin-Sperre den Eintritt in einen Blockierungszustand vermeiden und den Overhead reduzieren kann, erfordert sie einen Busy-Loop-Vorgang, um CPU-Zeit zu beanspruchen. Sie eignet sich nur für Szenarien, in denen der Sperrstatus gemeinsam genutzter Daten sehr kurz ist.

Adaptive Spin-Locks wurden in JDK 1.6 eingeführt. Adaptiv bedeutet, dass die Anzahl der Spins nicht mehr festgelegt ist, sondern von der vorherigen Anzahl von Spins auf demselben Schloss und dem Status des Schlossbesitzers bestimmt wird.

Schlossentfernung

Unter Sperrenbeseitigung versteht man die Beseitigung von Sperren für gemeinsam genutzte Daten, bei denen festgestellt wurde, dass es wahrscheinlich keine Konkurrenz gibt.

Die Beseitigung von Sperren wird hauptsächlich durch eine Escape-Analyse unterstützt. Wenn die gemeinsam genutzten Daten auf dem Heap nicht entkommen und von anderen Threads nicht aufgerufen werden können, können sie als private Daten behandelt und ihre Sperren aufgehoben werden.

Für einige Codes, die nicht gesperrt zu sein scheinen, werden tatsächlich viele Sperren implizit hinzugefügt. Beispielsweise fügt der folgende String-Splicing-Code implizit eine Sperre hinzu:

​public static String concatString(String s1, String s2, String s3) { return s1 + s2 + s3 }

String ist eine unveränderliche Klasse und der Compiler optimiert automatisch das Spleißen von String. Vor JDK 1.5 wurde dies in eine kontinuierliche append()-Operation eines StringBuffer-Objekts umgewandelt:

public static String concatString(String s1, String s2, String s3) { StringBuffer sb = new StringBuffer(s1); return sb.toString(); ; }

In jeder append()-Methode gibt es einen Synchronisationsblock. Die virtuelle Maschine beobachtet die Variable sb und stellt schnell fest, dass ihr dynamischer Bereich innerhalb der concatString()-Methode eingeschränkt ist. Das heißt, dass jede Referenz auf sb niemals außerhalb der concatString()-Methode entweicht und für andere Threads nicht zugänglich ist, sodass sie entfernt werden kann.

Aufrauen sperren

Wenn eine Reihe aufeinanderfolgender Vorgänge dasselbe Objekt wiederholt sperren und entsperren, führen häufige Sperrvorgänge zu Leistungseinbußen.

Die aufeinanderfolgenden append()-Methoden im Beispielcode im vorherigen Abschnitt fallen in diese Kategorie. Wenn die virtuelle Maschine erkennt, dass dasselbe Objekt durch eine solche Reihe fragmentierter Vorgänge gesperrt ist, erweitert (grob) sie den Sperrbereich nach außerhalb der gesamten Vorgangssequenz. Der Beispielcode im vorherigen Abschnitt wird von vor der ersten append()-Operation bis nach der letzten append()-Operation erweitert, sodass er nur einmal gesperrt werden muss.

​Leichtes Schloss

Mit JDK 1.6 wurden vorgespannte Schlösser und leichte Schlösser eingeführt, sodass Schlösser vier Zustände haben können: entsperrt, vorgespannt, leicht gesperrt und schwer gesperrt.

Schwere Sperren werden üblicherweise auch als synchronisierte Objektsperren bezeichnet.

Das Folgende ist das Speicherlayout des HotSpot-Objektheaders der virtuellen Maschine. Diese Daten werden als Mark Word bezeichnet. Die Tag-Bits entsprechen fünf Zuständen, die in der Zustandstabelle rechts aufgeführt sind. Zusätzlich zu dem für GC markierten Status wurden die anderen vier Status bereits zuvor eingeführt.

Die linke Seite der folgenden Abbildung zeigt den Stapel der virtuellen Maschine eines Threads. Es gibt einen Teil des Bereichs namens Lock Record, der während des laufenden Prozesses der Lightweight-Sperre erstellt und zum Speichern des Markierungsworts des Lock-Objekts verwendet wird. Auf der rechten Seite befindet sich ein Sperrobjekt, das Mark Word und andere Informationen enthält.

Im Vergleich zu herkömmlichen Schwergewichtssperren verwenden Leichtgewichtssperren CAS-Operationen, um den Mehraufwand von Schwergewichtssperren mithilfe von Mutexen zu vermeiden. Bei den meisten Sperren gibt es während des gesamten Synchronisationszyklus keine Konkurrenz, daher besteht keine Notwendigkeit, Mutexe für die Synchronisierung zu verwenden. Wenn CAS fehlschlägt, können Sie stattdessen Mutexe verwenden.

Wenn beim Versuch, ein Sperrobjekt abzurufen, das Sperrobjekt mit 0 01 markiert ist, bedeutet dies, dass sich das Sperrobjekt in einem entsperrten Zustand befindet. Zu diesem Zeitpunkt erstellt die virtuelle Maschine einen Sperrdatensatz im Stapel der virtuellen Maschine des aktuellen Threads und verwendet dann die CAS-Operation, um das Markierungswort des Objekts auf den Sperrdatensatzzeiger zu aktualisieren. Wenn die CAS-Operation erfolgreich ist, erhält der Thread die Sperre für das Objekt und das Markierungswort-Sperrtag des Objekts ändert sich in 00, was darauf hinweist, dass sich das Objekt in einem leichten Sperrstatus befindet.

Wenn die CAS-Operation fehlschlägt, prüft die virtuelle Maschine zunächst, ob das Markierungswort des Objekts auf den Stapel der virtuellen Maschine des aktuellen Threads verweist. Wenn dies der Fall ist, bedeutet dies, dass der aktuelle Thread bereits Eigentümer des Sperrobjekts ist, und kann dann direkt darauf zugreifen Andernfalls bedeutet dies, dass das Sperrobjekt von anderen Threads blockiert wurde. Wenn mehr als zwei Threads um dieselbe Sperre konkurrieren, ist die leichte Sperre nicht mehr wirksam und muss zu einer schweren Sperre erweitert werden.

Vorspannungssperre

Die Idee der voreingenommenen Sperre besteht darin, den ersten Thread zum Erfassen des Sperrobjekts zu bevorzugen. Dieser Thread muss nach dem Erlangen der Sperre keine Synchronisierungsvorgänge mehr durchführen, und selbst CAS-Vorgänge sind nicht mehr erforderlich.

Wenn das Sperrobjekt zum ersten Mal vom Thread erfasst wird, wechselt es in den voreingenommenen Zustand und wird als 1 01 markiert. Verwenden Sie gleichzeitig die CAS-Operation, um die Thread-ID in Mark Word aufzuzeichnen. Wenn die CAS-Operation erfolgreich ist, muss dieser Thread nicht jedes Mal einen Synchronisationsvorgang ausführen, wenn er in den mit dieser Sperre verbundenen Synchronisationsblock eintritt.

Wenn ein anderer Thread versucht, dieses Sperrobjekt abzurufen, wird der Bias-Status beendet. Zu diesem Zeitpunkt wird der Bias (Revoke Bias) aufgehoben und in den entsperrten Zustand oder den Lightweight-Sperrzustand zurückversetzt.

Das obige ist der detaillierte Inhalt vonSo optimieren Sie Java-Sperren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen