Frage Wann genau verwenden Sie das flüchtige Schlüsselwort in Java?


Ich habe gelesen "Wann sollte 'flüchtig' in Java verwendet werden?"Aber ich bin immer noch verwirrt. Woher weiß ich, wann ich eine Variable als volatil markieren sollte? Was passiert, wenn ich es falsch verstehe, entweder ein flüchtiges auf etwas zu verzichten, das es benötigt, oder etwas auf etwas zu setzen, das nicht funktioniert? Was sind die Regeln? der Daumen, wenn Sie herausfinden, welche Variablen in Multithread-Code flüchtig sein sollten?


76
2017-08-15 18:36


Ursprung


Antworten:


Sie verwenden es im Grunde, wenn Sie eine Membervariable durch mehrere Threads aufrufen lassen möchten, aber keine zusammengesetzte Atomizität benötigen (nicht sicher, ob dies die richtige Terminologie ist).

class BadExample {
    private volatile int counter;

    public void hit(){
        /* This operation is in fact two operations:
         * 1) int tmp = this.counter;
         * 2) this.counter = tmp + 1;
         * and is thus broken (counter becomes fewer
         * than the accurate amount).
         */
        counter++;
    }
}

Das oben genannte ist ein schlechtes Beispiel, weil Sie brauchen zusammengesetzte Atomarität.

 class BadExampleFixed {
    private int counter;

    public synchronized void hit(){
        /*
         * Only one thread performs action (1), (2) at a time
         * "atomically", in the sense that other threads can not 
         * observe the intermediate state between (1) and (2).
         * Therefore, the counter will be accurate.
         */
        counter++;
    }
}

Nun zu einem gültigen Beispiel:

 class GoodExample {
    private static volatile int temperature;

    //Called by some other thread than main
    public static void todaysTemperature(int temp){
        // This operation is a single operation, so you 
        // do not need compound atomicity
        temperature = temp;
    }

    public static void main(String[] args) throws Exception{
        while(true){
           Thread.sleep(2000);
           System.out.println("Today's temperature is "+temperature);
        }
    }
}

Nun, warum kannst du es nicht einfach benutzen? private static int temperature? In der Tat können Sie (in dem Sinne, dass Ihr Programm nicht explodieren oder etwas), aber die Änderung zu temperature durch den anderen Thread kann oder kann nicht für den Haupt-Thread "sichtbar" sein.

Grundsätzlich bedeutet dies, dass es sogar möglich ist, dass Ihre App. schreibt weiter Today's temperature is 0 für immer wenn du nicht benutzen volatile (In der Praxis tendiert der Wert dazu, irgendwann sichtbar zu werden. Sie sollten jedoch nicht riskieren, bei Bedarf nicht flüchtig zu verwenden, da dies zu fiesen Fehlern führen kann (verursacht durch vollständig konstruierte Objekte usw.).

Wenn Sie setzen volatile Schlüsselwort auf etwas, das nicht benötigt wird volatileEs hat keinen Einfluss auf die Korrektheit Ihres Codes (d. h. das Verhalten ändert sich nicht). In Bezug auf die Leistung hängt dies von der JVM-Implementierung ab. Theoretisch kann es zu einer kleinen Leistungsverschlechterung kommen, da der Compiler keine Neuordnungsoptimierungen durchführen kann, CPU-Cache ungültig machen muss, aber der Compiler könnte wiederum beweisen, dass auf Ihr Feld niemals mehrere Threads zugreifen können und den Effekt von volatile Schlüsselwort vollständig und kompiliere es nach identischen Anweisungen.

BEARBEITEN:
Antwort zu diesem Kommentar:

Ok, aber warum können wir die heutige Temperatur nicht synchronisieren und einen synchronisierten Temperaturgetter erstellen?

Sie können und es wird sich richtig verhalten. Alles, was du kannst volatile kann mit gemacht werden synchronizedaber nicht umgekehrt. Es gibt zwei Gründe, die Sie vielleicht bevorzugen volatile Wenn du kannst:

  1. Weniger Fehler anfällig: Dies hängt vom Kontext ab, aber in vielen Fällen mit volatile ist weniger anfällig für Nebenläufigkeitsfehler, wie Blockieren beim Halten des Schlosses, Deadlocks usw.
  2. Leistungsfähiger: In den meisten JVM-Implementierungen volatile kann einen wesentlich höheren Durchsatz und eine bessere Latenz haben. In den meisten Anwendungen ist der Unterschied jedoch zu gering, um wichtig zu sein.

94
2017-08-15 18:56



Volatile ist in Lock-Free-Algorithmen am nützlichsten. Sie markieren die Variable, die freigegebene Daten enthält, als flüchtig, wenn Sie Sperren nicht für den Zugriff auf diese Variable verwenden und Änderungen eines Threads in einem anderen sichtbar sein sollen oder eine "Folge-nach-Beziehung" erstellen möchten, um sicherzustellen, dass die Berechnung erfolgt nicht erneut bestellt, um sicherzustellen, dass Änderungen zum richtigen Zeitpunkt sichtbar werden.

Das JMM Kochbuch beschreibt, welche Operationen neu geordnet werden können und welche nicht.


14
2017-08-15 18:38



Das volatile kann auch verwendet werden, um unveränderbare Objekte in einer Umgebung mit mehreren Threads sicher zu veröffentlichen.

Ein Feld wie deklarieren public volatile ImmutableObject foo stellt sicher, dass alle Threads immer die aktuell verfügbare Instanzreferenz sehen.

Sehen Java Concurrency in der Praxis für mehr zu diesem Thema.


5
2017-08-15 19:12



volatile  Das Schlüsselwort garantiert, dass der Wert der flüchtigen Variablen immer aus dem Hauptspeicher und nicht aus dem lokalen Cache von Thread gelesen wird.

Von Java Concurrency Anleitung  :

Die Verwendung von flüchtigen Variablen reduziert das Risiko von Speicherkonsistenzfehlern, da jedes Schreiben in eine flüchtige Variable eine Vor-Vor-Beziehung mit nachfolgenden Lesevorgängen derselben Variablen festlegt

Dies bedeutet, dass Änderungen an einer flüchtigen Variablen immer für andere Threads sichtbar sind. Es bedeutet auch, dass wenn ein Thread eine flüchtige Variable liest, er nicht nur die letzte Änderung der flüchtigen, sondern auch die Nebenwirkungen des Codes sieht, der zu der Änderung geführt hat.

Bezüglich Ihrer Anfrage:

Woher weiß ich, wann ich eine Variable volatil markieren soll? Was sind die Faustregeln, wenn Sie herausfinden, welche Variablen in Multithread-Code flüchtig sein sollten?

Wenn Sie glauben, dass alle Reader-Threads immer den letzten Wert einer Variablen erhalten, müssen Sie die Variable als markieren volatile

Wenn Sie einen Writer-Thread haben, um den Wert von Variablen- und mehreren Leser-Threads zu ändern, um den Wert der Variablen zu lesen, flüchtiger Modifikator garantiert Speicherkonsistenz.

Wenn Sie mehrere Threads zum Schreiben und Lesen von Variablen haben, volatile Modifier alleine garantiert keine Speicherkonsistenz. Sie müssen synchronize der Code oder verwenden Sie hohe Ebene Nebenläufigkeit konstruiert wie Locks, Concurrent Collections, Atomic variables etc.

Verwandte SE Fragen / Artikel:

Volatile variable Erklärung in Java-Dokumenten

Unterschied zwischen Volatilität und Synchronisation in Java

javarevisited Artikel


4
2017-09-05 16:43



Ich stimme tatsächlich nicht mit dem Beispiel überein, das in der Antwort mit der höchsten Wahl gegeben wurde NICHT die volatile Semantik nach dem Java-Speichermodell richtig darstellen. Volatile hat eine viel komplexere Semantik.

In diesem Beispiel könnte der Hauptthread weiterhin "Heute ist Temperatur 0" für immer drucken, auch wenn ein anderer Thread ausgeführt wird, der die Temperatur aktualisieren soll, wenn dieser andere Thread nie geplant wird.

Eine bessere Möglichkeit, flüchtige Semantiken zu veranschaulichen, sind zwei Variablen.

Der Einfachheit halber werden wir annehmen, dass die einzige Möglichkeit zur Aktualisierung der beiden Variablen die Methode ist "SetTemperaturen".

Der Einfachheit halber werden wir annehmen, dass nur 2 Threads laufen, Haupt-Thread und Thread 2.

//volatile variable
private static volatile int temperature; 
//any other variable, could be volatile or not volatile doesnt matter.
private static int yesterdaysTemperature
//Called by other thread(s)
public static void setTemperatures(int temp, int yestemp){
    //thread updates yesterday's temperature
    yesterdaysTemperature = yestemp;
    //thread updates today's temperature. 
    //This instruction can NOT be moved above the previous instruction for optimization.
    temperature = temp;
   }

die letzten beiden Zuweisungsanweisungen können NICHT zu Optimierungszwecken entweder durch den Compiler, die Laufzeit oder die Hardware neu geordnet werden.

public static void main(String[] args) throws Exception{
    while(true){
       Thread.sleep(2000);
       System.out.println("Today's temperature is "+temperature); 
       System.out.println("Yesterday's temperature was "+yesterdaysTemperature );
 }
}

Sobald der Hauptthread die Temperatur der flüchtigen Variablen liest (während sie gedruckt wird),

1) Es gibt eine Garantie, dass es das sehen wird zuletzt geschrieben Wert dieser flüchtigen Variablen, unabhängig davon, wie viele Threads sie schreiben, unabhängig davon, bei welcher Methode sie aktualisiert wird, ob synchronisiert oder nicht.

2) Wenn die Anweisung system.out im Hauptthread ausgeführt wird, nach In dem Zeitpunkt, in dem Thread 2 die Anweisung temperature = temp ausgeführt hat, werden sowohl die gestrige Temperatur als auch die aktuelle Temperatur garantiert die Werte ausgeben, die in Thread 2 festgelegt wurden, wenn die Anweisung temperature = temp ausgeführt wurde.

Diese Situation bekommt ein MENGE komplexer, wenn a) mehrere Threads ausgeführt werden und b) Es gibt andere Methoden als nur die Methode setTemperature, die die Variable der gestrigen Temperatur und der aktuellen Temperatur aktualisieren können, die von diesen anderen Threads aktiv aufgerufen werden. Ich denke, es würde einen Artikel von anständiger Größe brauchen, um die Implikationen zu analysieren, basierend darauf, wie das Java Memory Model die flüchtige Semantik beschreibt.

Kurz gesagt, der Versuch, flüchtig für die Synchronisation zu verwenden, ist extrem riskant, und Sie wären besser daran, Ihre Methoden zu synchronisieren.


3
2018-01-14 16:39



http://mindprod.com/jgloss/volatile.html

"Das Schlüsselwort volatile wird für Variablen verwendet, die von anderen Threads gleichzeitig geändert werden können."

"Da andere Threads lokale Variablen nicht sehen können, müssen lokale Variablen niemals flüchtig markiert werden. Sie müssen synchronisiert werden, um Änderungen an Variablen aus verschiedenen Threads zu koordinieren, aber oft werden sie sich nur flüchtig verhalten, um sie zu betrachten."


2
2017-08-15 18:38



voltalie Means Ändert den Wert. Der Wert dieser Variablen wird niemals Thread-lokal zwischengespeichert: Alle Lese- und Schreibvorgänge werden direkt in den "Hauptspeicher" verschoben. Mit anderen Worten Java-Compiler und Thread, die den Wert dieser Variablen nicht zwischenspeichern und immer lesen es aus dem Hauptspeicher.


2
2018-04-11 05:29