37 Stimmen

Schlüsselwort Volatile in Java - Klarstellung

Ich bin wirklich verwirrt über das, was ich über die Anwendungen des volatile-Schlüsselworts in Java gelesen habe.

  1. Ist die folgende Aussage korrekt? "Eine Schreiboperation auf ein volatiles Feld findet vor jedem folgenden Lesezugriff auf dasselbe Feld statt"

  2. Wann sollte idealerweise das volatile-Schlüsselwort verwendet werden?

  3. Was ist der Unterschied zwischen:

    class TestClass
    {  private int x;
    
       synchronized int get(){return x;}
       synchronized void set(int x){this.x = x;}
    
    }

und

class TestClass
{  private volatile int x;

   int get(){return x;}
   void set(int x){this.x = x;}

}

0 Stimmen

Um meinen automatischen "Möglicherweise" -Duplikatkommentar zu klären: Die beiden vorherigen Vorschläge waren keine exakten Duplikate; meiner ist es. Es ist wichtig, dass diese Frage als Duplikat geklärt wird, da die ausgewählte Antwort auf diese Frage gefährlich falsch ist, wenn sie behauptet, dass volatil nur das als volatil deklarierte Feld betrifft - tatsächlich führt ein Schreib- und ein Lesebefehl von jeder volatilen Variable eine vollständige Speicherbarriere ein, genauso wie die Synchronisation zwischen zwei Threads.

64voto

Kerem Baydoğan Punkte 10172

volatile ist ein Feldmodifikator, während synchronized Codeblöcke und Methoden modifiziert. Daher können wir drei Variationen eines einfachen Zugriffsmethode unter Verwendung dieser beiden Schlüsselwörter angeben:

     int i1;
     int geti1() {return i1;}

     volatile int i2;
     int geti2() {return i2;}

     int i3;
     synchronized int geti3() {return i3;}

geti1() greift auf den aktuell in i1 im aktuellen Thread gespeicherten Wert zu. Threads können lokale Kopien von Variablen haben, und die Daten müssen nicht mit den Daten in anderen Threads übereinstimmen. Insbesondere ein anderer Thread kann i1 in seinem Thread aktualisiert haben, aber der Wert im aktuellen Thread könnte sich von diesem aktualisierten Wert unterscheiden. Tatsächlich hat Java die Idee eines "Haupt"-Speichers, der den aktuellen "korrekten" Wert für Variablen speichert. Threads können ihre eigene Kopie von Daten für Variablen haben, und die Thread-Kopie kann sich vom "Haupt"-Speicher unterscheiden. Tatsächlich ist es also möglich, dass der "Haupt"-Speicher einen Wert von 1 für i1 hat, Thread1 einen Wert von 2 für i1 hat und Thread2 einen Wert von 3 für i1 hat, wenn Thread1 und Thread2 beide i1 aktualisiert haben, aber diese aktualisierten Werte noch nicht im "Haupt"-Speicher oder anderen Threads propagiert wurden.

Andererseits greift geti2() effektiv auf den Wert von i2 im "Haupt"-Speicher zu. Eine volatile Variable darf keine lokale Kopie einer Variablen haben, die sich vom aktuell im "Haupt"-Speicher gehaltenen Wert unterscheidet. Effektiv muss eine als volatile deklarierte Variable ihre Daten zwischen allen Threads synchronisieren, sodass immer, wenn Sie die Variable in einem Thread zugreifen oder aktualisieren, alle anderen Threads sofort den gleichen Wert sehen. Im Allgemeinen haben volatile Variablen einen höheren Zugriffs- und Aktualisierungsüberhead als "normale" Variablen. Normalerweise dürfen Threads ihre eigene Kopie von Daten haben, um die Effizienz zu steigern.

Es gibt zwei Unterschiede zwischen volitile und synchronized.

Erstens erwerben und lösen synchronized Sperren für Monitore, die nur einen Thread dazu zwingen können, einen Codeblock gleichzeitig auszuführen. Das ist der ziemlich bekannte Aspekt von synchronized. Aber synchronized synchronisiert auch den Speicher. Tatsächlich synchronisiert synchronized den gesamten Thread-Speicher mit dem "Haupt"-Speicher. Das Ausführen von geti3() macht Folgendes:

  1. Der Thread erwirbt die Sperre für den Monitor für das Objekt "this".
  2. Der Thread-Speicher leert alle seine Variablen, d.h. er liest effektiv alle seine Variablen aus dem "Haupt"-Speicher.
  3. Der Codeblock wird ausgeführt (in diesem Fall wird der Rückgabewert auf den aktuellen Wert von i3 gesetzt, der gerade aus dem "Haupt"-Speicher zurückgesetzt worden sein könnte).
  4. (Alle Änderungen an Variablen würden normalerweise jetzt in den "Haupt"-Speicher geschrieben, aber für geti3() haben wir keine Änderungen.)
  5. Der Thread gibt die Sperre für den Monitor für das Objekt "this" frei.

Während also volatile nur den Wert einer Variablen zwischen Thread-Speicher und "Haupt"-Speicher synchronisiert, synchronisierte den Wert aller Variablen zwischen Thread-Speicher und "Haupt"-Speicher und sperrt und löst einen Monitor. Offensichtlich hat synchronized wahrscheinlich mehr Overhead als volatile.

http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html

6voto

Steve Kuo Punkte 60196

volatile garantiert, dass Lesevorgänge von der Variable immer den aktuellsten Wert widerspiegeln. Die Laufzeit kann dies auf verschiedene Weisen erreichen, z. B. indem der Wert nicht zwischengespeichert oder der Zwischenspeicher aktualisiert wird, wenn sich der Wert geändert hat.

2voto

John Vint Punkte 38604

Bwawok deutete darauf hin, aber das volatile-Schlüsselwort ist nicht nur für die Speichersichtbarkeit. Bevor Java 1.5 veröffentlicht wurde, erklärte das volatile-Schlüsselwort, dass das Feld jedes Mal den neuesten Wert des Objekts erhält, indem es den Hauptspeicher für Lesevorgänge anspricht und für Schreibvorgänge gespült wird.

Das heutige volatile-Schlüsselwort besagt zwei sehr wichtige Dinge:

  1. Machen Sie sich keine Sorgen darüber, wie, sondern wissen Sie, dass Sie beim Lesen eines volatilen Feldes immer den aktuellsten Wert haben werden.
  2. Ein Compiler kann ein volatiles Lese-/Schreibvorgang nicht neu anordnen, um die Programmreihenfolge beizubehalten.

0voto

gawi Punkte 13510

Vom Standpunkt eines Clients aus ist ein privates volatiles Feld vor der öffentlichen Schnittstelle verborgen, während synchronisierte Methoden sichtbarer sind.

0voto

Alexander Pogrebnyak Punkte 43705

Um Teil 3 Ihrer Frage zu beantworten, und teilweise Teil 2.

Es gibt keinen funktionalen Unterschied zwischen den Beispielen synchronized und volatile.

Jedoch haben beide ihre eigenen Nachteile in Bezug auf die Leistung. In einigen Fällen kann die Leistung von volatile wirklich schlechter sein als einfach nur synchronized oder andere Primitiven aus java.util.concurrent zu verwenden. Für weitere Diskussionen dazu siehe -> Warum sind Variablen in Java standardmäßig nicht volatil?.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X