801 Stimmen

Wofür ist das Schlüsselwort volatile nützlich?

Bei der Arbeit stieß ich heute auf die volatile Schlüsselwort in Java. Da ich damit nicht sehr vertraut bin, fand ich diese Erklärung .

Wenn man bedenkt, wie detailliert in diesem Artikel das betreffende Schlüsselwort erklärt wird, haben Sie es dann jemals verwendet oder könnten Sie sich einen Fall vorstellen, in dem Sie dieses Schlüsselwort auf die richtige Art und Weise verwenden könnten?

4voto

MB. Punkte 7147

Ja, ich benutze es ziemlich viel - es kann sehr nützlich für Multi-Thread-Code sein. Der Artikel, auf den Sie hingewiesen haben, ist ein guter Artikel. Allerdings gibt es zwei wichtige Dinge zu beachten:

  1. Sie sollten volatile nur verwenden, wenn Sie vollständig verstehen, was es bewirkt und wie es sich von synchronized unterscheidet. In vielen Situationen erscheint volatile, oberflächlich betrachtet, eine einfachere und leistungsfähigere Alternative zu synchronized zu sein, obwohl oft ein besseres Verständnis von volatile deutlich machen würde deutlich machen würde, dass synchronized die einzige Option ist, die funktionieren würde.
  2. volatile funktioniert eigentlich nicht in einer vielen älteren JVMs, obwohl synchronisiert funktioniert. Ich erinnere mich, ein Dokument gesehen zu haben, das auf die verschiedenen Stufen der Unterstützung in verschiedenen JVMs hinwies, aber leider kann ich es jetzt nicht mehr finden. Schauen Sie sich das auf jeden Fall an, wenn Sie Java vor 1.5 verwenden oder wenn Sie keine Kontrolle über die JVMs haben, auf denen Ihr Programm laufen wird.

4voto

dgvid Punkte 25475

Absolut, ja. (Und nicht nur in Java, sondern auch in C#.) Es gibt Fälle, in denen Sie einen Wert abrufen oder setzen müssen, der auf der gegebenen Plattform garantiert eine atomare Operation ist, z. B. ein int oder boolean, aber nicht den Overhead von Thread Locking benötigt. Mit dem Schlüsselwort volatile können Sie sicherstellen, dass Sie beim Lesen des Wertes die aktuell Wert und nicht einen zwischengespeicherten Wert, der gerade durch einen Schreibvorgang in einem anderen Thread veraltet ist.

3voto

Mohan Punkte 4179

Es gibt zwei verschiedene Verwendungen des Schlüsselworts volatile.

  1. Verhindert, dass die JVM Werte aus Registern (z.B. Cache) liest und erzwingt, dass der Wert aus dem Speicher gelesen wird.
  2. Reduziert das Risiko von Fehlern bei der Speicherkonsistenz.

Verhindert, dass die JVM die Werte in den Registern liest, und zwingt ihre Wert aus dem Speicher zu lesen.

A Besetztzeichen wird verwendet, um zu verhindern, dass ein Thread fortgesetzt wird, während das Gerät beschäftigt ist und das Kennzeichen nicht durch eine Sperre geschützt ist:

while (busy) {
    /* do something else */
}

Der Test-Thread wird fortgesetzt, wenn ein anderer Thread die Besetztzeichen :

busy = 0;

Da jedoch im Test-Thread häufig auf busy zugegriffen wird, kann die JVM den Test optimieren, indem sie den Wert von busy in einem Register ablegt und dann den Inhalt des Registers testet, ohne den Wert von busy vor jedem Test im Speicher zu lesen. Der Test-Thread würde niemals sehen, dass sich busy ändert, und der andere Thread würde nur den Wert von busy im Speicher ändern, was zu einem Deadlock führen würde. Das Deklarieren der Besetztzeichen als flüchtig bezeichnet, muss ihr Wert vor jedem Test gelesen werden.

Reduziert das Risiko von Speicherkonsistenzfehlern.

Die Verwendung volatiler Variablen verringert das Risiko von Speicherkonsistenzfehler weil jedes Schreiben in eine flüchtige Variable eine "passiert-vor" Beziehung zu den nachfolgenden Messwerten derselben Variablen. Dies bedeutet, dass Änderungen an einer flüchtigen Variablen immer für andere Threads sichtbar sind.

Die Technik des Lesens und Schreibens ohne Speicherkonsistenzfehler wird als atomare Wirkung .

Eine atomare Aktion ist eine Aktion, die tatsächlich auf einmal stattfindet. Eine atomare Aktion kann nicht mittendrin aufhören: Entweder geschieht sie vollständig oder gar nicht. Die Nebenwirkungen einer atomaren Aktion sind nicht sichtbar, bis die Aktion abgeschlossen ist.

Nachfolgend finden Sie Aktionen, die Sie angeben können und die atomar sind:

  • Lesen und Schreiben sind atomar für Referenzvariablen und für die meisten primitiven Variablen (alle Typen außer long und double).
  • Lese- und Schreibvorgänge sind für alle deklarierten Variablen atomar flüchtig (einschließlich Long- und Double-Variablen).

Zum Wohl!

2voto

sankar banerjee Punkte 91

Volatile macht folgendes.

1> Das Lesen und Schreiben von flüchtigen Variablen durch verschiedene Threads erfolgt immer aus dem Speicher, nicht aus dem thread-eigenen Cache oder CPU-Register. Jeder Thread arbeitet also immer mit dem neuesten Wert. 2> Wenn 2 verschiedene Threads mit derselben Instanz oder statischen Variablen im Heap arbeiten, kann es sein, dass der eine die Aktionen des anderen als nicht in Ordnung ansieht. Siehe den Blog von Jeremy Manson dazu. Aber volatile hilft hier.

Der folgende vollständig ausgeführte Code zeigt, wie eine Reihe von Threads in einer vordefinierten Reihenfolge ausgeführt und Ausgaben gedruckt werden können, ohne das Schlüsselwort synchronized zu verwenden.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

Um dies zu erreichen, können wir den folgenden vollwertigen, laufenden Code verwenden.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

Der folgende Github-Link enthält eine Readme-Datei, die eine genaue Erklärung enthält. https://github.com/sankar4git/volatile_thread_ordering

1voto

Ravindra babu Punkte 45577

Aus der Oracle-Dokumentation Seite entsteht der Bedarf an flüchtigen Variablen, um Probleme mit der Speicherkonsistenz zu beheben:

Die Verwendung flüchtiger Variablen verringert das Risiko von Fehlern bei der Speicherkonsistenz, da jeder Schreibvorgang in eine flüchtige Variable eine "happens-before"-Beziehung zu nachfolgenden Lesevorgängen in derselben Variable herstellt.

Dies bedeutet, dass Änderungen an einem volatile Variable sind immer für andere Threads sichtbar. Das bedeutet auch, dass ein Thread, wenn er eine flüchtige Variable liest, nicht nur die letzte Änderung an der volatile sondern auch die Nebeneffekte des Codes, der zu der Änderung geführt hat.

Wie erklärt in Peter Parker Antwort, in Ermangelung von volatile Modifikator kann jeder Thread-Stack seine eigene Kopie der Variable haben. Indem man die Variable als volatile Die Probleme mit der Speicherkonsistenz wurden behoben.

Werfen Sie einen Blick auf jenkov Tutorial-Seite zum besseren Verständnis.

Schauen Sie sich die verwandte SE-Frage an, um mehr Details über volatile und Anwendungsfälle für volatile zu erfahren:

Unterschied zwischen volatile und synchronized in Java

Ein praktischer Anwendungsfall:

Sie haben viele Threads, die z.B. die aktuelle Zeit in einem bestimmten Format ausgeben müssen: java.text.SimpleDateFormat("HH-mm-ss") . Yon kann eine Klasse haben, die die aktuelle Zeit in SimpleDateFormat und aktualisierte die Variable alle eine Sekunde. Alle anderen Threads können diese flüchtige Variable einfach verwenden, um die aktuelle Zeit in Protokolldateien zu drucken.

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