Ist es richtig zu sagen, dass static
eine Kopie des Wertes für alle Objekte und volatile
eine Kopie des Wertes für alle Threads bedeutet?
Wie auch immer, ein static
Variablenwert wird auch ein Wert für alle Threads sein. Warum sollten wir dann volatile
wählen?
Das Deklarieren einer statischen Variablen in Java bedeutet, dass nur eine Kopie vorhanden ist, unabhängig davon, wie viele Objekte der Klasse erstellt werden. Auf die Variable kann auch dann zugegriffen werden, wenn überhaupt kein Objects
erstellt wurde. Threads können jedoch lokal zwischengespeicherte Werte haben.
Wenn eine Variable flüchtig und nicht statisch ist, gibt es eine Variable für jedes Object
. An der Oberfläche scheint es also keinen Unterschied zu einer normalen Variablen zu geben, sondern einen Unterschied zu static . Selbst mit Object
-Feldern kann ein Thread einen Variablenwert lokal zwischenspeichern.
Das heißt, wenn zwei Threads gleichzeitig eine Variable desselben Objekts aktualisieren und die Variable nicht als flüchtig deklariert wird, kann es vorkommen, dass einer der Threads einen alten Wert im Cache hat.
Selbst wenn Sie über mehrere Threads auf einen statischen Wert zugreifen, kann für jeden Thread eine lokal zwischengespeicherte Kopie vorhanden sein! Um dies zu vermeiden, können Sie die Variable als static volatile deklarieren. Dadurch wird der Thread jedes Mal gezwungen, den globalen Wert zu lesen.
volatile ist jedoch kein Ersatz für eine ordnungsgemäße Synchronisation!
Zum Beispiel:
private static volatile int counter = 0;
private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
Das mehrmalige Ausführen von concurrentMethodWrong
kann zu einem von Null verschiedenen Endwert des Zählers führen!
Um das Problem zu lösen, müssen Sie eine Sperre implementieren:
private static final Object counterLock = new Object();
private static volatile int counter = 0;
private void concurrentMethodRight() {
synchronized (counterLock) {
counter = counter + 5;
}
//do something
synchronized (counterLock) {
counter = counter - 5;
}
}
Oder verwenden Sie die Klasse AtomicInteger
.
Unterschied zwischen statisch und flüchtig:
Statische Variable: Wenn zwei Threads (angenommen t1
und t2
) greifen auf dasselbe Objekt zu und aktualisieren eine Variable, die als statisch deklariert ist, dann bedeutet dies t1
und t2
kann eine eigene lokale Kopie desselben Objekts (einschließlich statischer Variablen) in ihrem jeweiligen Cache erstellen. Die Aktualisierung erfolgt also durch t1
für die statische Variable in ihrem lokalen Cache spiegelt sich nicht in der statischen Variablen für t2
Zwischenspeicher .
Statische Variablen werden im Kontext von Object verwendet, wobei die Aktualisierung, die von einem Objekt vorgenommen wird, in allen anderen Objekten derselben Klasse wiedergegeben wird jedoch nicht im Kontext von Thread wobei die Aktualisierung von Ein Thread für die statische Variable spiegelt die Änderungen sofort für alle Threads (in ihrem lokalen Cache) wider.
Flüchtige Variable: Wenn zwei Threads (angenommen t1
und t2
) greifen auf dasselbe Objekt zu und aktualisieren eine Variable, die als flüchtig deklariert ist, dann bedeutet dies t1
und t2
kann einen eigenen lokalen Cache des Objekts erstellen mit Ausnahme der als flüchtig deklarierten Variablen. Die flüchtige Variable hat also nur eine Hauptkopie, die von verschiedenen Threads aktualisiert wird, und die Aktualisierung, die ein Thread für die flüchtige Variable vornimmt, wird sofort für den anderen Thread übernommen.
Zusätzlich zu anderen Antworten möchte ich ein Bild hinzufügen (Bild macht leicht verständlich)
static
Variablen können für einzelne Threads zwischengespeichert werden. In einer Umgebung mit mehreren Threads werden die zwischengespeicherten Daten eines Threads möglicherweise nicht für andere Threads übernommen, da eine Kopie vorhanden ist.
Die volatile
-Deklaration stellt sicher, dass Threads die Daten nicht zwischenspeichern und verwendet nur die gemeinsam genutzte Kopie .
Ich denke, static
und volatile
haben überhaupt keine Beziehung. Ich schlage vor, Sie lesen Java Tutorial zu verstehen Atomic Access , und warum Atomic Access verwenden, verstehen, was Interleaved ist, werden Sie Antwort finden.
In einfachen Worten,
statisch : static
Variablen werden eher mit class verknüpft als bei jedem Objekt. Jede Instanz der Klasse hat eine gemeinsame Klassenvariable, die sich an einer festen Stelle im Speicher befindet
volatile : Dieses Schlüsselwort gilt sowohl für class als auch für Instanz Variablen.
Die Verwendung flüchtiger Variablen verringert das Risiko von Speicherkonsistenzfehlern, da jedes Schreiben in eine flüchtige Variable eine Beziehung herstellt, die vor dem Lesen derselben Variablen erfolgt. Dies bedeutet, dass Änderungen an einer flüchtigen Variablen für andere Threads immer sichtbar sind
Schauen Sie sich das an Artikel von Javin Paul
um flüchtige Variablen besser zu verstehen.
Fehlt das Schlüsselwort volatile
, kann der Wert der Variablen im Stapel der einzelnen Threads unterschiedlich sein. Indem Sie die Variable als volatile
definieren, erhalten alle Threads den gleichen Wert im Arbeitsspeicher, und Fehler bei der Speicherkonsistenz wurden vermieden.
Hier kann der Ausdruck variable
entweder static
(Klassen-) Variable oder instance
(Objekt-) Variable sein.
Zu Ihrer Anfrage:
Ein statischer Variablenwert wird auch ein Wert für alle Threads sein. Warum sollten wir uns dann für volatile entscheiden?
Wenn ich in meiner Anwendung die Variable instance
benötige, kann ich die Variable static
nicht verwenden. Selbst im Fall der Variablen static
kann die Konsistenz aufgrund des im Diagramm gezeigten Thread-Cache nicht garantiert werden.
Die Verwendung von volatile
-Variablen verringert das Risiko von Speicherkonsistenzfehlern, da jedes Schreiben in eine flüchtige Variable eine Beziehung herstellt, die vor dem Lesen derselben Variablen erfolgt. Dies bedeutet, dass Änderungen an einer flüchtigen Variablen für andere Threads immer sichtbar sind.
Darüber hinaus bedeutet dies, dass ein Thread beim Lesen einer flüchtigen Variablen nicht nur die letzte Änderung der flüchtigen Variable sieht, sondern auch die Nebenwirkungen des Codes, der die Änderung ausgelöst hat => Speicherkonsistenzfehler sind bei flüchtigen Variablen weiterhin möglich. Um Nebenwirkungen zu vermeiden, müssen Sie synchronisierte Variablen verwenden. In Java gibt es jedoch eine bessere Lösung.
Der Zugriff auf einfache atomare Variablen ist effizienter als der Zugriff auf diese Variablen über synchronisierten Code
Einige der Klassen in der Java.util.concurrent
package stellt atomare Methoden bereit, die nicht auf der Synchronisation beruhen.
Weitere Informationen finden Sie in diesem High Level Concurrency Control Artikel.
Schauen Sie sich insbesondere Atomvariablen an.
Verwandte SE-Fragen:
der flüchtige Zugriff auf variable Werte erfolgt direkt aus dem Hauptspeicher. Es sollte nur in Multithreading-Umgebungen verwendet werden. statische Variable wird einmal geladen. Wenn es in einer Umgebung mit einem einzelnen Thread verwendet wird, wird die Kopie der Variablen aktualisiert, und der Zugriff darauf ist nicht schädlich, da nur ein Thread vorhanden ist.
Wenn nun eine statische Variable in einer Multithreading-Umgebung verwendet wird, treten Probleme auf, wenn das gewünschte Ergebnis erwartet wird. Da jeder Thread eine eigene Kopie hat, spiegelt sich jedes Inkrementieren oder Dekrementieren der statischen Variablen eines Threads möglicherweise nicht in einem anderen Thread wider.
wenn man erwartet, dass die statische Variable die gewünschten Ergebnisse liefert, dann wird alles aufgelöst, wenn man die statische Variable in Multithreading verwendet.
Nicht sicher, ob statische Variablen im lokalen Thread-Speicher oder NICHT zwischengespeichert sind. Aber als ich zwei Threads (T1, T2) ausgeführt habe, die auf dasselbe Objekt (obj) zugegriffen haben, und als die Aktualisierung durch den T1-Thread auf eine statische Variable erfolgte, spiegelte sich dies in T2 wider.