In diesem Artikel werden hauptsächlich relevante Informationen zum Implementierungsprinzip von Volatile in Java vorgestellt. Bei der Multithread-Parallelität spielen sowohl synchronisiert als auch Volatile eine wichtige Rolle „Sichtbarkeit“ von gemeinsam genutzten Variablen in der Multiprozessorentwicklung. Freunde, die dies benötigen, können sich auf das
Implementierungsprinzip von Volatile in Java High Concurrency
Zusammenfassung: Sowohl synchronisiert als auch flüchtig spielen eine wichtige Rolle in der gleichzeitigen Multithread-Programmierung. Volatile ist ein leichtgewichtiges synchronisiertes System, das die gemeinsame Nutzung von Variablen in der Multiprozessorentwicklung gewährleistet. Sichtbarkeit bedeutet, dass ein anderer Thread den geänderten Wert lesen kann, wenn ein Thread eine gemeinsam genutzte Variable ändert. Es hat in einigen Fällen weniger Overhead als synchronisiert
Java
Programmierspracheermöglicht Threads den Zugriff auf Shares-Variablen, Um sicherzustellen, dass gemeinsam genutzte Variablen genau und konsistent aktualisiert werden können, sollten Threads sicherstellen, dass diese Variable individuell durch eine exklusive Sperre erfasst wird. Die Java-Sprache bietet flüchtige Funktionen, was in manchen Situationen praktischer ist als Sperren. Wenn ein Feld als flüchtig deklariert ist, stellt das Java-Thread-Speicher--Modell sicher, dass alle Threads den gleichen Wert dieser Variablen sehen
2. Prinzip der flüchtigen ImplementierungWie sorgt Volatile also für Sichtbarkeit? Verwenden Sie unter dem x86-Prozessor Tools, um die vom JIT-Compiler generierten Assembleranweisungen abzurufen und zu sehen, was die CPU beim Schreiben in Volatile tun wird.
Java-Code: Instanz = new Singleton();//Instanz ist eine flüchtige VariableAssembly-Code: 0x01a3de1d: movb $0x0,0x1104800(% esi );0x01a3de24: lock ad
dl $0x0,(%esp);Wenn eine gemeinsam genutzte Variable, die mit einer flüchtigen Variablen geändert wurde, geschrieben wird, wird eine zweite Zeile Assemblercode geschrieben hinzugefügt werden. Wenn wir das IA-32
Architecture-Softwareentwicklerhandbuch überprüfen, können wir sehen, dass die Sperrpräfixanweisung bei Mehrkernprozessoren zwei Dinge verursacht. Die Daten der Zeile
Cachedes aktuellen Prozessors werden in den Systemspeicher zurückgeschrieben. Dieser Rückschreibvorgang führt dazu, dass die an dieser Speicheradresse in anderen CPUs zwischengespeicherten Daten ungültig werden.
Um die Verarbeitungsgeschwindigkeit zu erhöhen, kommuniziert der Prozessor nicht direkt mit dem Speicher, sondern liest die Daten im Systemspeicher zunächst in den internen Cache (L1, L2 oder andere), bevor er den Vorgang ausführt , aber es weiß nicht, wann der Vorgang in den Speicher geschrieben wird. Wenn ein Schreibvorgang für eine deklarierte flüchtige Variable ausgeführt wird, sendet die JVM eine Lock-Präfixanweisung an den Prozessor, um die Daten in den Cache zu schreiben Zeile, in der sich die Variable befindet, zurück in den Systemspeicher. Aber selbst wenn es in den Speicher zurückgeschrieben wird und die von anderen Prozessoren zwischengespeicherten Werte noch alt sind, kommt es bei der Ausführung von Berechnungsvorgängen zu Problemen. Daher muss bei Multiprozessoren sichergestellt werden, dass die Caches jedes Prozessors vorhanden sind Sind konsistent, wird die Cache-Konsistenz erreicht. Jeder Prozessor prüft, ob sein zwischengespeicherter Wert abgelaufen ist, indem er die auf den Bus hochgeladenen Daten abhört Die Cache-Zeile des Prozessors wird auf den ungültigen
-Zustand gesetzt. Wenn der Prozessor diese Daten ändern möchte, wird er gezwungen, die Daten erneut aus dem Systemspeicher in den Prozessor-Cache zu lesen. Anweisungen zum Sperren von Präfixen bewirken, dass der Prozessor-Cache zurück in den Speicher geschrieben wird. Der Lock-Präfix-Befehl bewirkt, dass das LOCK#-Signal des Prozessors während der Ausführung des Befehls aktiviert wird. In einer Multiprozessorumgebung stellt das LOCK#-Signal sicher, dass der Prozessor exklusiven Zugriff auf den gemeinsam genutzten Speicher hat, während das Signal aktiviert ist. (Da dadurch der Bus gesperrt wird und andere CPUs nicht auf den Bus zugreifen können. Wenn kein Zugriff auf den Bus möglich ist, bedeutet dies, dass nicht auf den Systemspeicher zugegriffen werden kann.) Bei neueren Prozessoren sperrt das LOCK#-Signal den Bus jedoch im Allgemeinen nicht , sperrt aber den Cache. Immerhin ist der Bus-Overhead relativ groß. Die Auswirkungen von Sperrvorgängen auf den Prozessor-Cache werden in Kapitel 8.1.4 detailliert beschrieben. Bei Intel486- und Pentium-Prozessoren wird das LOCK#-Signal während Sperrvorgängen immer auf dem Bus aktiviert. Wenn jedoch bei P6- und neueren Prozessoren der Speicherbereich, auf den zugegriffen wird, bereits im Prozessor zwischengespeichert ist, wird das LOCK#-Signal nicht aktiviert. Stattdessen wird der Cache dieses Speicherbereichs gesperrt und in den Speicher zurückgeschrieben. Dabei wird der Cache-Konsistenzmechanismus verwendet, um die Atomizität der Änderung sicherzustellen. Dieser Vorgang wird als „Cache-Sperrung“ bezeichnet Geändert. Speicherbereichsdaten, die von mehr als zwei Prozessoren zwischengespeichert werden. Durch das Zurückschreiben des Caches eines Prozessors in den Speicher wird der Cache anderer Prozessoren ungültig. Der IA-32-Prozessor und der Intel 64-Prozessor verwenden das MESI-Steuerungsprotokoll (Modify, Exclusive, Shared, Invalidate), um die Kohärenz zwischen dem internen Cache und anderen Prozessor-Caches aufrechtzuerhalten. Beim Betrieb auf Multi-Core-Prozessorsystemen können IA-32- und Intel 64-Prozessoren den Zugriff anderer Prozessoren auf den Systemspeicher und ihre internen Caches abhören. Sie verwenden Sniffing-Techniken, um sicherzustellen, dass die Daten im internen Cache, im Systemspeicher und in anderen Prozessor-Caches auf dem Bus konsistent bleiben. Wenn beispielsweise bei Prozessoren der Pentium- und P6-Familie ein Prozessor ausgespuckt wird, um zu erkennen, dass ein anderer Prozessor beabsichtigt, an eine Speicheradresse zu schreiben, die derzeit den gemeinsam genutzten Status verarbeitet, macht der Sniffing-Prozessor seine Cache-Zeile wie folgt ungültig: Füllung der Cache-Zeile erzwingen
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in das Implementierungsprinzip von Volatile in Java mit hoher Parallelität. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!