Volatile vs Static in Java

265

Ist es richtig zu sagen, dass staticdies eine Kopie des Wertes für alle Objekte und bedeutet?volatile eine Kopie des Werts für alle Threads bedeutet?

Wie auch immer, ein staticvariabler Wert wird auch ein Wert für alle Threads sein. Warum sollten wir uns dann dafür entscheiden volatile?

Jothi
quelle
Offizielle Erklärung von volatile: cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
Vadzim

Antworten:

365

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 sie überhaupt nicht Objectserstellt wurde. Threads können jedoch lokal zwischengespeicherte Werte haben.

Wenn eine Variable flüchtig und nicht statisch ist , gibt es für jede eine Variable Object. An der Oberfläche scheint es also keinen Unterschied zu einer normalen Variablen zu geben, aber völlig anders als statisch . Aber auch mitObject Feldern kann ein Thread einen Variablenwert lokal zwischenspeichern.

Dies bedeutet, dass, wenn zwei Threads gleichzeitig eine Variable desselben Objekts aktualisieren und die Variable nicht als flüchtig deklariert wird, es vorkommen kann, dass einer der Threads einen alten Wert im Cache hat.

Selbst wenn Sie über mehrere Threads auf einen statischen Wert zugreifen , kann jeder Thread seine lokale zwischengespeicherte Kopie haben! Um dies zu vermeiden, können Sie die Variable als statisch flüchtig deklarieren. Dadurch wird der Thread gezwungen, jedes Mal den globalen Wert zu lesen.

Jedoch flüchtig ist kein Ersatz für eine richtige Synchronisation!
Zum Beispiel:

private static volatile int counter = 0;

private void concurrentMethodWrong() {
  counter = counter + 5;
  //do something
  counter = counter - 5;
}

Die concurrentMethodWronggleichzeitige gleichzeitige Ausführung kann zu einem Endwert des Zählers führen, der von Null abweicht!
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 benutze die AtomicIntegerKlasse.

stivlo
quelle
7
Der flüchtige Modifikator garantiert, dass jeder Thread, der ein Feld liest, den zuletzt geschriebenen Wert sieht. Wenn die Variable von mehreren Threads gemeinsam genutzt wird und Sie diese Funktion benötigen, hängt dies von Ihrem Anwendungsfall ab.
Stivlo
5
Was ist der Cache, wenn Sie "lokal zwischengespeichert" sagen? CPU-Cache, eine Art JVM-Cache?
Mert Inan
6
@mertinan Ja, die Variable kann sich in einem Cache befinden, der näher am Prozessor oder Kern liegt. Weitere Informationen finden Sie unter cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html .
Stivlo
15
'flüchtig' bedeutet nicht 'eine Variable pro Objekt'. Das Fehlen von "statisch" macht das. -1, weil dieses elementare Missverständnis des OP nicht geklärt wurde.
Marquis von Lorne
27
@EJP Ich dachte, dass der Satz "Eine Variable als flüchtig deklarieren, es wird eine Variable für jedes Objekt geben. An der Oberfläche scheint es also keinen Unterschied zu einer normalen Variablen zu geben" erklärte, dass ich hinzugefügt habe und nicht statisch , Sie können den Artikel jederzeit bearbeiten und den Wortlaut verbessern, um dies klarer zu machen.
Stivlo
288

Unterschied zwischen statisch und flüchtig:

Statische Variable : Wenn zwei Threads (annehmen t1und t2) sind das gleiche Objekt zuzugreifen und eine Variable zu aktualisieren , die als statisch deklariert wird , dann bedeutet das t1und t2kann ihre eigene lokale Kopie des gleichen Objekts (einschließlich statischen Variablen) in ihrem jeweiligen Cache machen, so Update Die von t1der statischen Variablen in ihrem lokalen Cache erstellte wird nicht in der statischen Variablen für den t2Cache wiedergegeben.

Statische Variablen werden im Kontext von Object verwendet, bei dem die von einem Objekt vorgenommenen Aktualisierungen in allen anderen Objekten derselben Klasse wiedergegeben werden , nicht jedoch im Kontext von Thread, bei dem die Aktualisierung eines Threads auf die statische Variable die Änderungen an allen Objekten sofort widerspiegelt Threads (in ihrem lokalen Cache).

Volatile Variable : Wenn zwei Threads (annehmen t1und t2) sind das gleiche Objekt zuzugreifen und eine Variable zu aktualisieren , die als flüchtiges deklariert wird , dann bedeutet das t1und t2kann ihre eigene lokale Cache des Objekts macht mit Ausnahme der Variable , die als flüchtigen deklariert wird . Die flüchtige Variable hat also nur eine Hauptkopie, die von verschiedenen Threads aktualisiert wird, und die von einem Thread vorgenommene Aktualisierung der flüchtigen Variablen wird sofort auf den anderen Thread übertragen.

So M
quelle
6
Hallo @Som, bitte korrigiere mich, wenn ich falsch liege. Aber denken Sie nicht, dass die Anweisung " aber nicht im Kontext von Thread, in der die Aktualisierung eines Threads auf die statische Variable die Änderungen an allen Threads (in ihrem lokalen Cache) sofort widerspiegelt " "sein sollte", aber nicht im Kontext of Thread, bei dem die Aktualisierung eines Threads auf die statische Variable << NICHT >> die Änderungen sofort an allen Threads (in ihrem lokalen Cache) widerspiegelt. "
Jaikrat
@Jaikrat Ja das war sehr verwirrend für mich. Mein Verständnis ist, dass Sie Recht haben und dass diese Antwort falsch ist, wie geschrieben. Ich möchte auch korrigiert werden, wenn ich falsch liege.
Stuart
@Jaikrat Threads speichern die statischen Variablen zwar nicht zwischen, beziehen sich jedoch auf die aktualisierten statischen Variablen.
Som
@Som Dann möchten Sie den Para korrigieren und entfernen, aber nicht im Kontext von Thread . Das ist sehr verwirrend. Vielen Dank
Jaikrat
Leider ist diese Antwort falsch. Auf modernen CPUs kann sogar eine volatileVariable von verschiedenen CPU-Caches gemeinsam genutzt werden. Dies stellt kein Problem dar, da der Cache das ausschließliche Eigentum an der Cache-Zeile aushandelt, bevor diese geändert wird.
David Schwartz
32

Neben anderen Antworten möchte ich ein Bild dafür hinzufügen (Bild leicht verständlich)

Geben Sie hier die Bildbeschreibung ein

staticVariablen können für einzelne Threads zwischengespeichert werden. Wenn in einer Umgebung mit mehreren Threads ein Thread seine zwischengespeicherten Daten ändert, wird dies möglicherweise nicht für andere Threads angezeigt, da sie eine Kopie davon haben.

volatileDurch die Deklaration wird sichergestellt, dass Threads die Daten nicht zwischenspeichern und nur die freigegebene Kopie verwenden .

Bildquelle

mrsrinivas
quelle
1
statische Variablen werden von Objekten unter einem Thread gemeinsam genutzt? Hierbei sollten statische Variablen gelesen werden, die von allen Objekten gemeinsam genutzt werden, unabhängig von Threads.
Cquezel
1
"Flüchtige Variablen werden von mehreren Threads gemeinsam genutzt (also auch von Objekten)." Volatile ändert nichts daran, wie Variablen von mehreren Threads oder Objekten gemeinsam genutzt werden. Es ändert, wie die Laufzeit den Wert zwischenspeichern darf.
Cquezel
1
Ihr Kommentar zu statischen Variablen gilt auch für nicht statische Variablen. "Wird zwischengespeichert" und "wird nicht wiedergegeben" sollte wahrscheinlich umformuliert werden "wird möglicherweise zwischengespeichert" und "wird möglicherweise nicht wiedergegeben".
Cquezel
4
Ich war sehr verwirrt. Dieses Bild hat alle meine Fragen geklärt!
Vins
5

Ich denke staticund volatilehabe überhaupt keine Beziehung. Ich schlage vor, Sie lesen das Java-Tutorial, um Atomic Access zu verstehen , und warum Sie Atomic Access verwenden, verstehen, was verschachtelt ist , werden Sie eine Antwort finden.

Amitābha
quelle
4

In einfachen Worten,

  1. statisch : staticVariablen sind der Klasse zugeordnet und nicht mit irgendwelchen Objekt zugeordnet . Jede Instanz der Klasse teilt eine Klassenvariable, die sich an einem festen Ort im Speicher befindet

  2. flüchtig : Dieses Schlüsselwort gilt sowohl für die Klasse als auch für Instanz - Variablen.

Die Verwendung flüchtiger Variablen verringert das Risiko von Speicherkonsistenzfehlern, da durch das Schreiben in eine flüchtige Variable eine Beziehung hergestellt wird, bevor sie mit nachfolgenden Lesevorgängen derselben Variablen in Beziehung steht. Dies bedeutet, dass Änderungen an einer flüchtigen Variablen für andere Threads immer sichtbar sind

Schauen Sie sich diesen Artikel von anJavin Paul , um flüchtige Variablen besser zu verstehen.

Geben Sie hier die Bildbeschreibung ein

In Abwesenheit von volatile kein Schlüsselwort vorhanden ist, kann der Wert der Variablen im Stapel jedes Threads unterschiedlich sein. Wenn Sie die Variable als volatilefestlegen, erhalten alle Threads den gleichen Wert in ihrem Arbeitsspeicher, und Speicherkonsistenzfehler wurden vermieden.

Hier der Begriff variable entweder static(Klassen-) Variable oder instance(Objekt-) Variable sein.

Zu Ihrer Anfrage:

Wie auch immer, ein statischer Variablenwert wird auch ein Wert für alle Threads sein. Warum sollten wir uns dann für flüchtig entscheiden?

Wenn ich instancein meiner Anwendung eine Variable benötige , kann ich diese nicht verwendenstatic Variable verwenden. Selbst im Fall einer staticVariablen kann die Konsistenz aufgrund des Thread-Cache nicht garantiert werden, wie in der Abbildung gezeigt.

Verwenden von volatile Variablen verringert das Risiko von Speicherkonsistenzfehlern, da bei jedem Schreiben in eine flüchtige Variable eine Beziehung hergestellt wird, die vor dem Lesen derselben Variablen auftritt. 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 Variablen sieht, sondern auch die Nebenwirkungen des Codes, der zur Änderung geführt hat => Speicherkonsistenzfehler sind bei flüchtigen Variablen weiterhin möglich . Um Nebenwirkungen zu vermeiden, müssen Sie synchronisierte Variablen verwenden. Aber es gibt eine bessere Lösung in Java.

Die Verwendung des einfachen Zugriffs auf atomare Variablen ist effizienter als der Zugriff auf diese Variablen über synchronisierten Code

Einige der Klassen im java.util.concurrentPaket bieten atomare Methoden, die nicht auf Synchronisation beruhen.

Weitere Informationen finden Sie in diesem Artikel zur Steuerung der Parallelität auf hoher Ebene .

Vor allem hat einen Blick auf Atomic Variablen .

Verwandte SE-Fragen:

Flüchtig gegen Atomic

Volatile Boolean vs AtomicBoolean

Unterschied zwischen flüchtig und synchronisiert in Java

Ravindra Babu
quelle
Ich schätze diese Antwort sehr. Ich wusste, was volatilefrüher war, aber diese Antwort verdeutlicht mir sehr, warum ich sie noch volatilemit der staticVariablen verwenden muss.
Chaklader Asfak Arefe
volatile: Dieses Schlüsselwort gilt sowohl für Klassen- als auch für Instanzvariablen. Die Aussage, die Sie oben gemacht haben, ist in Bezug auf die Klasse falsch. Nur zwei Schlüsselwörter, die für Variablen gelten, sind flüchtig und vorübergehend. so flüchtig wird nicht für die Klasse gelten.
ASR
flüchtig gilt für Klassenvariablen (statisch). Suchen Sie in Google nach doppelt gesperrten Singleton-Links, und Sie können feststellen, dass Ihr Verständnis falsch ist. stackoverflow.com/questions/18093735/…
Ravindra babu
private static volatile ist eine gültige Deklaration.
Ravindra Babu
0

Der Zugriff auf flüchtige Variablenwerte erfolgt direkt aus dem Hauptspeicher. Es sollte nur in Multithreading-Umgebungen verwendet werden. Die statische Variable wird einmal geladen. Wenn es in einer Umgebung mit einem Thread verwendet wird, auch wenn die Kopie der Variablen aktualisiert wird und der Zugriff darauf keinen Schaden anrichtet, da nur ein Thread vorhanden ist.

Wenn nun eine statische Variable in einer Multithreading-Umgebung verwendet wird, gibt es Probleme, wenn man das gewünschte Ergebnis davon erwartet. Da jeder Thread eine eigene Kopie hat, wird ein Inkrement oder Dekrement der statischen Variablen eines Threads möglicherweise nicht in einem anderen Thread angezeigt.

Wenn man die gewünschten Ergebnisse von einer statischen Variablen erwartet und dann flüchtig mit statisch im Multithreading verwendet, wird alles aufgelöst.

Aslam anwer
quelle
0

Nicht sicher, ob statische Variablen im lokalen Thread-Speicher zwischengespeichert werden oder NICHT. Aber als ich zwei Threads (T1, T2) ausgeführt habe, die auf dasselbe Objekt (obj) zugegriffen haben, und als der T1-Thread die statische Variable aktualisiert hat, wurde dies in T2 wiedergegeben.

user2779355
quelle
-2

Wenn wir eine Variable als statisch deklarieren, gibt es nur eine Kopie der Variablen. Wenn also verschiedene Threads auf diese Variable zugreifen, gibt es nur einen Endwert für die Variable (da der Variablen nur ein Speicherplatz zugewiesen ist).

Wenn eine Variable als flüchtig deklariert wird, haben alle Threads eine eigene Kopie der Variablen, aber der Wert wird aus dem Hauptspeicher übernommen. Daher ist der Wert der Variablen in allen Threads gleich.

In beiden Fällen ist der Hauptpunkt, dass der Wert der Variablen für alle Threads gleich ist.

Jitendra Nalwaya
quelle
15
Wenn eine Variable als flüchtig deklariert wird, haben alle Threads eine eigene Kopie der Variablen, der Wert wird jedoch aus dem Hauptspeicher übernommen. => richtig. Der Wert der Variablen in allen Threads ist also gleich. => falsch, jeder Thread verwendet denselben Wert für dasselbe Objekt, aber jedes Objekt hat seine eigene Kopie.
Stivlo