Wir wissen, dass die von Java implementierten gleichzeitigen Operationen endlich von unserer CPU abgeschlossen werden müssen. In der Zwischenzeit haben wir den Java -Quellcode in eine .Class -Datei zusammengestellt, dann geladen und dann von der Ausführungsmaschine der virtuellen Maschine ausgeführt, als Montagesprache interpretiert und dann in Betriebssystemanweisungen umgewandelt und dann in 1, 0 konvertiert und schließlich die CPU erkannt und ausgeführt.
Wenn wir die Parallelität von Java erwähnen, können wir nicht anders, als an gemeinsame Schlüsselwörter in Java zu denken: volatil und synchronisiert. Als nächstes werden wir sie aus diesen beiden Abschaltwörtern analysieren:
Das zugrunde liegende Implementierungsprinzip des flüchtigen Bestandteils
Implementierungsprinzipien und Anwendungen von synchronisiert
flüchtig
Der Interviewer ist die Lieblingsfrage in Java -Interviews. Wenn wir es sehen, ist das erste, was wir denken, die Sichtbarkeit zwischen Threads aufrechtzuerhalten. Es handelt sich um ein leichtes synchrones Synchron, das in einigen Fällen synchronisiert wird.
Die Rolle von volatilen:
Für eine durch volatile modifizierte Variable stellt das Java -Speichermodell sicher, dass die von allen Threads beobachteten variablen Werten konsistent sind.
Wie flüchtig funktioniert:
Wir können eine flüchtige Variable definieren, die Werte zuweisen und Tools verwenden, um die vom JIT -Compiler erzeugten Montageanweisungen zu erhalten. Wir werden feststellen, dass beim Schreiben in die flüchtige Variable eine zusätzliche Anweisung vorliegt: eine mit Sperre vorangestellte Anweisung:
Das Sperrenprefix-Befehl führt zu zwei Dingen an den Multi-Core-Prozessor:
① Schreiben Sie die Daten der aktuellen Prozessor -Cache -Zeile in den Speicher zurück.
②Dieis-Rückschrittspeicherbetrieb wird die von den Daten zwischengespeicherte Speicher-Speicheradresse in anderen CPUs ungültig machen.
Wenn wir die oben genannten zwei Punkte kennen, fällt es uns nicht schwer, den Mechanismus der Volatie -Variablen zu verstehen.
Unter mehreren Prozessoren wird ein Cache -Konsistenzprotokoll implementiert, um sicherzustellen, dass der Cache jedes Prozessors konsistent ist. Jeder Prozessor schnüffelt die propagierten Daten im Bus, um zu überprüfen, ob der zwischengespeicherte Wert abgelaufen ist.
synchronisiert
Wenn ich an die Parallelität von Multi-Thread nachdenke, denke das erste, woran ich denke, synchronisiert. Als Synchronisation übersetzt. Wir alle wissen, dass es sich um ein Schwergewichtsschloss handelt. Wenn ein Thread für eine Methode oder einen Codeblock verwendet wird, fallen andere Threads in einen suspendierten Zustand, der in Java im Schlafzustand erscheint. Wir alle wissen, dass die Aussetzung und Laufzeit des Threads in den Kernel -Zustand des Betriebssystems übertragen werden muss (der entsprechende Kernelstaat ist der Benutzerzustand), der besonders die CPU -Ressourcen verschwendet, sodass dieses Schwergewichtsschloss wahrhaft ist!
Nach Java SE 1.6 hat das Java -Wartungsteam jedoch eine Reihe von Optimierungen durchgeführt (diese Optimierungen werden nacheinander diskutiert), sodass es nicht so "schwer" ist, und das Wiedereintrittschloss, das in der Vergangenheit Vorteile hatte, ist weniger vorteilhaft geworden (Reentrantlock).
Sprechen wir über synchronisiert in den folgenden Aspekten:
Die Grundlagen der Synchronisierung, um die Synchronisation zu erreichen
Wie synchronisierte implementiert das Sperren
Positives Schloss, leichtes Schloss (Spin -Lock), Schwergewichtsschloss
Schloss Upgrade
So implementieren Sie Atomoperationen in Java
①Die Grundlagen der synchronisierten Synchronisation:
Wir können in der Entwicklung oder im Java -Quellcode synchronisiert sehen, wie Hashtable, StringBuilder und an anderen Orten. Es gibt zwei allgemeine Möglichkeiten:
Ⅰ Synchronisationsmethode
Die Synchronisationsmethode muss nur vor der Methode synchronisiert werden. Wenn ein Thread es ausführt, fallen andere Threads in das Warten, bis er das Schloss veröffentlicht. Die Verwendung von Methoden kann in zwei Typen unterteilt werden: für gewöhnliche Synchronisationsmethoden und für statische Methoden. Der Unterschied zwischen ihnen besteht darin, dass die verschlossenen Objekte unterschiedlich sind. Die gesperrte Position gewöhnlicher Methoden ist das aktuelle Objekt, und die gesperrte Position statischer Methoden ist das Klassenobjekt der aktuellen Klasse.
Ⅱ Synchronisationsmethode Block
Die Synchronisationsmethode blockiert das Objekt, das nach der Synchronisierung in den Klammern konfiguriert ist. Dieses Objekt kann ein Wert und jede Variable oder jedes Objekt sein.
②How Synchronisierte Implementierungsperre:
In der JVM -Spezifikation können Sie das Implementierungsprinzip von Synchronized in JVM sehen. JVM implementiert die Synchronisation von Synchronisationsmethoden und Codeblöcken basierend auf dem Eingeben und Verlassen des Monitorobjekts. Der Codeblock wird mithilfe von Monitenter- und Monitorexit -Anweisungen implementiert. Die Synchronisationsmethode ist in der JVM -Spezifikation nicht spezifisch angegeben. Ich glaube jedoch, dass die spezifischen Prinzipien unterschiedlich sein sollten. Es ist nichts weiter, als den Java -Quellcode in eine Klassendatei zu kompilieren und die synchronisierte Methode in der Klassen -Bytecode -Datei zu markieren. Diese Methode wird synchronisiert, wenn die Bytecode -Engine diese Methode ausführt.
③Deflection Lock, Leichtes Schloss (Spinschloss), Schwergewichtsschloss:
Bevor wir über Schlösser sprechen, müssen wir den Java -Objektheader und den Java -Objekt -Header kennen:
Das durch synchronisierte Schloss wird im Java -Objekt -Header gespeichert. Der Java -Objekt -Header verfügt über 32bit/64bit (abhängig von der Anzahl der Bits des Betriebssystems) Längen -Markwort speichert den HashCode und sperren Informationen des Objekts. In MarkWord gibt es 2 -Bit -Leerzeichen, um den Zustand der Sperre 00, 01, 10, 11 darzustellen, die leichte Schlösser, Vorspannungsschlösser, Schwergewichtsschlösser und GC -Markierungen darstellen.
Positives Schloss: Positive Schloss wird als exzentrisches Schloss bezeichnet. Aus dem Namen können wir sehen, dass es sich um ein Schloss handelt, das zu einem bestimmten Thread tendiert.
In der tatsächlichen Entwicklung haben wir festgestellt, dass die Mehrheit der Multi-Thread-Parallelität, die meisten Synchronisationsmethoden durch denselben Thread durchgeführt werden, und die Wahrscheinlichkeit mehrerer Threads, die um eine Methode konkurrieren, ist relativ niedrig. Daher wird die wiederholte Erfassung und Freisetzung von Schlössern viel Ressourcenabfall verursacht. Daher wird die Vorspannungsschloss eingeführt, damit der Faden eine Schloss zu geringeren Kosten erhält. Wenn ein Thread auf einen Synchronisationsblock zugreift und eine Sperre erfasst, wird die Gewinde -ID der Vorspannungsschloss im Sperrdatensatz im Stapelrahmen des Objektkopfs und -Fadens gespeichert. Wenn der Thread in Zukunft in den Synchronisationsblock eingeht und verlässt, muss er keine CAS -Operationen ausführen, um das Sperren und Freischalten zu entsperren. Es ist nur notwendig zu prüfen, ob eine Vorspannungssperrung auf das aktuelle Markwort im Objektheader hinweist (in MarkWord gibt es ein BIAS -Sperrflag -Bit, um anzuzeigen, ob das aktuelle Objekt die Verzerrungsperrung unterstützt. Wir können den JVM -Parameter verwenden, um die Vorspannungssperrung festzulegen).
In Bezug auf die Veröffentlichung von voreingenommenen Schlössern verwenden vorgespannte Schlösser den Mechanismus zur Freigabe des Schlosses, bis der Wettbewerb vorliegt. Daher wird der Gewinde, der das vorgespannte Schloss hält, das Schloss freigesetzt, wenn andere Fäden versuchen, um vorgespannte Schlösser zu konkurrieren.
Hinweis: In Java6, 7 wird die Vorspannungsschloss standardmäßig gestartet
Leichtes Schloss:
Eine leichte Schloss ist, dass JVM vor der Ausführung des Synchronisationsblocks einen Speicherplatz zum Speichern des Sperrdatensatzes im Stapelrahmen des aktuellen Threads erstellt und das Markwort im Objektheader in ihn kopiert. Anschließend versucht der Thread, das Markwort im Objektheader durch einen Zeiger auf den Sperrdatensatz zu ersetzen. Wenn es erfolgreich ist, erhält der aktuelle Thread das Schloss. Wenn es fehlschlägt, bedeutet dies, dass andere Threads um das Schloss konkurrieren und der aktuelle Faden dreht, um das Schloss zu erhalten.
④ Upgrade des Sperrens:
Wenn der aktuelle Thread die oben genannte Methode nicht versuchen kann, um das Schloss zu erhalten, bedeutet dies, dass sich das aktuelle Schloss im Wettbewerb befindet und das Schloss auf ein Schwergewichtsschloss aktualisiert wird.
Der Unterschied zwischen leichtem Schloss und voreingenommenem Schloss:
Leichte Schlösser verwenden CAS -Operationen, um Mutexes ohne Wettbewerb zu beseitigen, während voreingenommene Schlösser die gesamte Synchronisation ohne Wettbewerb ohne Wettbewerb entfernen und selbst CAS -Operationen nicht durchgeführt werden!
⑤ So implementieren Sie atomare Operationen in Java:
Bevor wir verstehen, wie Java atomare Operationen implementiert, müssen wir wissen, wie Prozessoren Atomoperationen implementieren:
Die Prozessoren sind im Allgemeinen in zwei Möglichkeiten unterteilt, um Atomvorgänge auszuführen: Cache-Verriegelung und Busverriegelung, unter denen die Cache-Verriegelung besser ist, während die Busverriegelung eher ressourcenkonsumiert ist. (Wir werden hier nicht zu viel über die beiden Verriegelungsmethoden erklären, aber es werden detaillierte Erklärungen im Betriebssystem geben.)
Java verwendet (meistens) Schleifen CAS, um atomare Operationen zu implementieren. Die Verwendung von CAS zur Implementierung von Atomoperationen verursacht jedoch auch einige der folgenden klassischen Probleme:
1) ABA -Problem
Die AtomicStampedReference -Klasse wird in JDK zur Auflösung bereitgestellt (Bereitstellung der erwarteten Referenzen und erwarteten Flaggen)
2) lange Zykluszeit und hoher Overhead
Ich kann dies nicht lösen, dies ist ein häufiges Problem der Kreislauf
3) Es können nur atomare Operationen einer gemeinsam genutzten Variablen garantiert werden
In JDK wird eine Atomikreferenz bereitgestellt, um das Problem zu lösen, wobei mehrere gemeinsame Variablen in einer Klasse für CAS -Operationen platziert werden.