public class Demo09 { public static boolean flag = true; public static class T1 extends Thread { public T1(String name) { super(name); } @Override public void run() { System.out.println("线程" + this.getName() + " in"); while (flag) { ; } System.out.println("线程" + this.getName() + "停止了"); } } public static void main(String[] args) throws InterruptedException { new T1("t1").start(); //休眠1秒 Thread.sleep(1000); //将flag置为false flag = false; } }
Führen Sie den obigen Code aus und Sie werden feststellen, dass das Programm nicht beendet werden kann.
Es gibt eine Schleife in der run()-Methode von Thread t1. Das Flag wird verwendet, um zu steuern, ob der Hauptthread 1 Sekunde lang schläft und das Flag zu diesem Zeitpunkt auf false setzt t1 erkennt, dass das Flag falsch ist und gibt „Thread t1 gestoppt“ aus. Warum unterscheidet sich das Ergebnis von dem, was wir erwarten? Durch Ausführen des obigen Codes können wir feststellen, dass das in t1 angezeigte Flag immer wahr ist. Nachdem der Hauptthread das Flag auf „false“ gesetzt hat, wird es im t1-Thread nicht angezeigt, sodass die Schleife fortgesetzt wird.
Warum kann ich die vom Hauptthread in t1 geänderte Flagge nicht sehen?
Um dies zu erklären, müssen wir zunächst das Java Memory Model (JMM) verstehen. Die Kommunikation zwischen Java-Threads wird durch das Java Memory Model (in diesem Artikel als JMM bezeichnet) gesteuert Variable sollte in einen anderen Thread geschrieben werden. Ein Thread ist sichtbar. Aus abstrakter Sicht definiert JMM die abstrakte Beziehung zwischen Threads und dem Hauptspeicher: Gemeinsam genutzte Variablen zwischen Threads werden im Hauptspeicher (Hauptspeicher) gespeichert, und jeder Thread verfügt über einen privaten lokalen Speicher (lokaler Speicher), eine Kopie des gemeinsam genutzten Die Variable, die der Thread liest/schreibt, wird im lokalen Speicher gespeichert. Lokaler Speicher ist ein abstraktes Konzept von JMM und existiert nicht wirklich. Es umfasst Caches, Schreibpuffer, Register und andere Hardware- und Compiler-Optimierungen. Das abstrakte schematische Diagramm des Java-Speichermodells lautet wie folgt:
Wie aus der obigen Abbildung ersichtlich ist, muss Thread A mit Thread B kommunizieren und die folgenden 2 Schritte durchlaufen:
1. Thread A speichert den lokalen Speicher A. Die aktualisierten gemeinsam genutzten Variablen werden in den Hauptspeicher geleert
2 Anschließend geht Thread B in den Hauptspeicher, um die gemeinsam genutzten Variablen zu lesen, die Thread A zuvor aktualisiert hat. Das Folgende ist ein schematisches Diagramm Veranschaulichen Sie diese beiden Schritte:
Wie in der obigen Abbildung gezeigt, verfügen der lokale Speicher A und B über Kopien der gemeinsam genutzten Variablen x im Hauptspeicher. Angenommen, die x-Werte in diesen drei Speichern sind zunächst alle 0. Wenn Thread A ausgeführt wird, speichert er vorübergehend den aktualisierten x-Wert (vorausgesetzt, der Wert ist 1) in seinem eigenen lokalen Speicher A. Wenn Thread A und Thread B kommunizieren müssen, aktualisiert Thread A zunächst den geänderten x-Wert in seinem lokalen Speicher im Hauptspeicher. Zu diesem Zeitpunkt wird der x-Wert im Hauptspeicher 1. Anschließend geht Thread B zum Hauptspeicher, um den aktualisierten x-Wert von Thread A zu lesen. Zu diesem Zeitpunkt wird auch der x-Wert des lokalen Speichers von Thread B zu 1. Insgesamt handelt es sich bei diesen beiden Schritten im Wesentlichen darum, dass Thread A Nachrichten an Thread B sendet, und dieser Kommunikationsprozess muss über den Hauptspeicher erfolgen. JMM bietet Java-Programmierern Garantien für die Speichersichtbarkeit, indem es die Interaktion zwischen dem Hauptspeicher und dem lokalen Speicher jedes Threads steuert.
Nachdem wir JMM verstanden haben, schauen wir uns die Frage am Anfang des Artikels an. Warum können wir den Wert des Flags, das vom Hauptthread in Thread t1 geändert wurde, nicht sehen? Es gibt zwei Möglichkeiten:
1 . Nachdem der Hauptthread das Flag geändert hat, wird es nicht im Hauptspeicher aktualisiert, sodass t1 es nicht sehen kann
2 Der Hauptthread aktualisiert das Flag im Hauptspeicher, aber t1 liest weiterhin den Wert des Flags Arbeitsspeicher und geht nicht in den Hauptspeicher, um den neuesten Flag-Wert abzurufen
Gibt es für die beiden oben genannten Situationen eine Möglichkeit, das Problem zu lösen?Gibt es eine solche Methode: Nachdem der Thread die Kopie im Arbeitsspeicher geändert hat, wird sie jedes Mal, wenn die gemeinsam genutzte Variable im Arbeitsspeicher gelesen wird, sofort im Hauptspeicher aktualisiert und dann „In den Arbeitsspeicher kopieren“.
Java stellt uns eine solche Methode zur Verfügung. Durch die Verwendung von Volatile können durch Volatilität geänderte Variablen die folgenden Eigenschaften erzielen:
1 Lesen Sie den neuesten Wert der gemeinsam genutzten Variablen im Hauptspeicher und kopieren Sie ihn dann in den Arbeitsspeicher.
2 Der Thread ändert die Kopie der Variablen im Arbeitsspeicher. Nach der Änderung wird sie sofort im Hauptspeicher aktualisiert Speicher
Ändern wir den Anfang. Beispielcode:public volatile static boolean flag = true;
Verwenden Sie volatile, um die Flag-Variable zu ändern, und führen Sie dann das Programm aus. Die Ausgabe lautet:
线程t1 in 线程t1停止了
Das Programm kann jetzt normal anhalten. Volatile löst das Problem der Sichtbarkeit gemeinsamer Variablen in Multithreads. Sichtbarkeit bezieht sich darauf, ob die Änderung gemeinsamer Variablen durch einen Thread für einen anderen Thread sichtbar ist.
Das obige ist der detaillierte Inhalt vonWas sind die flüchtigen und Java-Speichermodelle für Java High Concurrency?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!