Einfachstes und verständliches Beispiel für ein flüchtiges Schlüsselwort in Java

75

Ich lese über flüchtige Schlüsselwörter in Java und verstehe den theoretischen Teil davon vollständig.

Was ich aber suche, ist ein gutes Fallbeispiel, das zeigt, was passieren würde, wenn die Variable nicht flüchtig wäre und wenn es so wäre.

Das folgende Code-Snippet funktioniert nicht wie erwartet (von hier aus ):

class Test extends Thread {

    boolean keepRunning = true;

    public void run() {
        while (keepRunning) {
        }

        System.out.println("Thread terminated.");
    }

    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println("keepRunning set to false.");
    }
}

Wenn der Thread keepRunningnicht flüchtig ist , sollte er im Idealfall unbegrenzt weiterlaufen. Aber es hört nach ein paar Sekunden auf.

Ich habe zwei grundlegende Fragen:

  • Kann jemand volatile mit Beispiel erklären? Nicht mit der Theorie von JLS.
  • Ist flüchtiger Ersatz für Synchronisation? Erreicht es Atomizität?
tmgr
quelle
Ein früherer Beitrag spricht ausführlich darüber stackackflow.com/questions/7212155/java-threading-volatile
AurA
3
Du denkst rückwärts. Wenn keepRunning nicht flüchtig war, sollte der Thread im Idealfall unbegrenzt weiterlaufen . Im Gegenteil: Durch Hinzufügen wird volatile garantiert, dass die Änderung am Feld sichtbar ist. Ohne das Schlüsselwort gibt es einfach überhaupt keine Garantien, alles kann passieren; Sie können nicht angeben, dass der Thread weiter ausgeführt werden soll [...] .
Bruno Reis
3
Hier ist die Sache: Speicher-Sichtbarkeitsfehler sind von Natur aus schwer (unmöglich?) Anhand eines einfachen Beispiels zu demonstrieren, das jedes Mal fehlschlägt. Angenommen, Sie haben eine Multi-Core-Maschine, wird Ihr Beispiel wahrscheinlich mindestens ein paar Mal fehlschlagen, wenn Sie es häufig ausführen (z. B. 1000 Läufe). Wenn Sie ein großes Programm haben - so dass beispielsweise das gesamte Programm und seine Objekte nicht in einen CPU-Cache passen - erhöht sich die Wahrscheinlichkeit, dass ein Fehler auftritt. Grundsätzlich sind Parallelitätsfehler so, dass, wenn die Theorie besagt, dass sie brechen können, dies wahrscheinlich der Fall sein wird, jedoch nur einmal alle paar Monate und wahrscheinlich in der Produktion.
Yshavit
Es gibt bereits ein gutes Beispiel für stackoverflow.com/questions/5816790/…
gurubelli
Hier ist ein Beispiel mit einem Artikel
Peter Lawrey

Antworten:

51

Flüchtig -> Garantiert Sichtbarkeit und NICHT Atomizität

Synchronisation (Sperren) -> Garantiert Sichtbarkeit und Atomizität (wenn richtig gemacht)

Flüchtig ist kein Ersatz für die Synchronisation

Verwenden Sie flüchtig nur, wenn Sie die Referenz aktualisieren und keine anderen Vorgänge ausführen.

Beispiel:

volatile int i = 0;

public void incrementI(){
   i++;
}

ist ohne Verwendung von Synchronisation oder AtomicInteger nicht threadsicher, da das Inkrementieren eine zusammengesetzte Operation ist.

Warum läuft das Programm nicht auf unbestimmte Zeit?

Nun, das hängt von verschiedenen Umständen ab. In den meisten Fällen ist JVM intelligent genug, um den Inhalt zu leeren.

Richtige Verwendung von flüchtigen Stoffen verschiedene mögliche Verwendungen von flüchtigen Bestandteilen. Die korrekte Verwendung von flüchtig ist schwierig. Ich würde sagen "Wenn Sie Zweifel haben, lassen Sie es weg", verwenden Sie stattdessen einen synchronisierten Block.

Ebenfalls:

Der synchronisierte Block kann anstelle von flüchtig verwendet werden, aber die Umkehrung ist nicht wahr .

Narendra Pathai
quelle
4
Das ist falsch. flüchtig garantiert atomare Natur. In der Oracle-Dokumentation ist dies eindeutig angegeben. Siehe docs.oracle.com/javase/tutorial/essential/concurrency/… .
Kalpa Gunarathna
4
Wenn wir in Java mehrere Threads haben, hat jeder Thread seinen eigenen Stapel (einen Speicherplatz) und init hat für jeden Thread eine eigene Kopie der Variablen, auf die er zugreifen kann. Wenn das flüchtige Schlüsselwort nicht vorhanden ist, um int i zu dekorieren, kann jeder Thread es in seinen Ausführungen verwenden. Wenn jeder Thread mit flüchtig deklariert ist, muss er den Wert von i aus / in den direkten Hauptspeicher lesen / schreiben, nicht in / von lokalen Kopien. In jeder Thread-Perspektive sind Operationen zur / von der Variablen i atomar.
Kalpa Gunarathna
atomicityEin Teil der Antwort ist verwirrend. Durch die Synchronisierung erhalten Sie gegenseitigen exklusiven Zugriff und Transparenz . volatilegibt nur Sichtbarkeit . Auch volatilemacht Lesen / Schreiben für longund doubleAtom (Synchronisation tut es auch durch ihre gegenseitige Ausschließlichkeit).
IlyaEremin
27

Für Ihr spezielles Beispiel: Wenn die Server-JVM nicht als flüchtig deklariert wird, kann sie die keepRunningVariable aus der Schleife herausheben, da sie in der Schleife nicht geändert wird (wodurch sie in eine Endlosschleife umgewandelt wird), die Client-JVM jedoch nicht. Deshalb sehen Sie unterschiedliche Ergebnisse.

Es folgt eine allgemeine Erklärung zu flüchtigen Variablen:

Wenn ein Feld deklariert wird volatile, werden der Compiler und die Laufzeit darauf hingewiesen, dass diese Variable gemeinsam genutzt wird und dass Operationen daran nicht mit anderen Speicheroperationen neu angeordnet werden sollten. Flüchtige Variablen werden nicht in Registern oder in Caches zwischengespeichert, in denen sie vor anderen Prozessoren verborgen sind. Daher gibt ein Lesevorgang einer flüchtigen Variablen immer den letzten Schreibvorgang eines Threads zurück .

Die Sichtbarkeitseffekte flüchtiger Variablen gehen über den Wert der flüchtigen Variablen selbst hinaus. Wenn Thread A in eine flüchtige Variable schreibt und anschließend Thread B dieselbe Variable liest, werden die Werte aller Variablen, die vor dem Schreiben in die flüchtige Variable für A sichtbar waren, nach dem Lesen der flüchtigen Variablen für B sichtbar.

Die häufigste Verwendung für flüchtige Variablen ist die Vervollständigung, Unterbrechung oder das Statusflag:

  volatile boolean flag;
  while (!flag)  {
     // do something untill flag is true
  }

Flüchtige Variablen können für andere Arten von Statusinformationen verwendet werden. Bei diesem Versuch ist jedoch mehr Sorgfalt erforderlich. Beispielsweise ist die Semantik von flüchtig nicht stark genug, um die Inkrementoperation ( count++) atomar zu machen, es sei denn, Sie können garantieren, dass die Variable nur von einem einzelnen Thread geschrieben wird.

Das Verriegeln kann sowohl Sichtbarkeit als auch Atomizität garantieren. flüchtige Variablen können nur die Sichtbarkeit garantieren.

Sie können flüchtige Variablen nur verwenden, wenn alle folgenden Kriterien erfüllt sind:

  • Schreibvorgänge in die Variable hängen nicht von ihrem aktuellen Wert ab, oder Sie können sicherstellen, dass nur ein einzelner Thread den Wert jemals aktualisiert.
  • Die Variable nimmt nicht an Invarianten mit anderen Zustandsvariablen teil. und
  • Das Sperren ist aus keinem anderen Grund erforderlich, während auf die Variable zugegriffen wird.

Debugging-Tipp : -serverGeben Sie beim Aufrufen der JVM auch beim Entwickeln und Testen immer den JVM-Befehlszeilenschalter an. Die Server-JVM führt mehr Optimierungen durch als die Client-JVM, z. B. das Hochheben von Variablen aus einer Schleife, die in der Schleife nicht geändert werden. Code, der möglicherweise in der Entwicklungsumgebung (Client-JVM) funktioniert, kann in der Bereitstellungsumgebung (Server-JVM) beschädigt werden.

Dies ist ein Auszug aus "Java Concurrency in Practice" , dem besten Buch, das Sie zu diesem Thema finden können.

Sokolov
quelle
15

Ich habe Ihr Beispiel leicht geändert. Verwenden Sie nun das Beispiel mit keepRunning als flüchtiges und nichtflüchtiges Mitglied:

class TestVolatile extends Thread{
    //volatile
    boolean keepRunning = true;

    public void run() {
        long count=0;
        while (keepRunning) {
            count++;
        }

        System.out.println("Thread terminated." + count);
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile();
        t.start();
        Thread.sleep(1000);
        System.out.println("after sleeping in main");
        t.keepRunning = false;
        t.join();
        System.out.println("keepRunning set to " + t.keepRunning);
    }
}
paritosht
quelle
Tolles Beispiel. Das hat bei mir perfekt funktioniert. ohne flüchtig auf keepRunning bleibt der Thread für immer hängen. Sobald Sie keepRunning als flüchtig markieren, stoppt es nach t.keepRunning = false;
Boris
4
Beispiel hat für mich funktioniert, habe nach Arbeitsbeispiel gesucht. +1, weil es mir geholfen hat und der Mangel an Erklärung nicht geschadet hat und die Abwertung nicht verdient hat.
John Doe
1
Hallo paritosht und @John Doe, könnten Sie uns erklären, warum Ihr Code ein funktionierendes Beispiel ist? Wenn mein Computer den in der Frage angegebenen Code mit oder ohne das flüchtige Schlüsselwort ausführt, stoppt er trotzdem.
Shanwu
Ich bekomme das gleiche Ergebnis mit und ohne votalitehier
WW
13

Was ist ein flüchtiges Schlüsselwort?

flüchtiges Schlüsselwort verhindert caching of variables.

Betrachten Sie den Code zunächst ohne flüchtiges Schlüsselwort

class MyThread extends Thread {
    private boolean running = true;   //non-volatile keyword

    public void run() {
        while (running) {
            System.out.println("hello");
        }
    }

    public void shutdown() {
        running = false;
    }
}

public class Main {

    public static void main(String[] args) {
        MyThread obj = new MyThread();
        obj.start();

        Scanner input = new Scanner(System.in);
        input.nextLine(); 
        obj.shutdown();   
    }    
}

Im Idealfall sollte dieses Programm print hellobis RETURN keygedrückt werden. Es some machineskann jedoch vorkommen, dass eine Variable ausgeführt wird cachedund Sie ihren Wert nicht über die Methode shutdown () ändern können, die zum infiniteDrucken von Hallo-Text führt.

So flüchtiges Schlüsselwort verwendet wird , ist es , guaranteeddass Ihr Variable wird nicht im Cache gespeichert wird, wird also run fineauf all machines.

private volatile boolean running = true;  //volatile keyword

Die Verwendung eines flüchtigen Schlüsselworts ist also a good und safer programming practice.

Prateek Joshi
quelle
7

Variable Volatile: Das flüchtige Schlüsselwort gilt für Variablen. Das Schlüsselwort volatile in Java garantiert, dass der Wert der flüchtigen Variablen immer aus dem Hauptspeicher und nicht aus dem lokalen Cache des Threads gelesen wird.

Access_Modifier volatile DataType Variable_Name;

Flüchtiges Feld: Ein Hinweis für die VM, dass mehrere Threads gleichzeitig versuchen können, auf den Wert des Felds zuzugreifen / ihn zu aktualisieren. Zu einer speziellen Art von Instanzvariablen, die von allen Threads mit geändertem Wert gemeinsam genutzt werden müssen. Ähnlich wie bei der statischen Variablen (Klasse) wird nur eine Kopie des flüchtigen Werts im Hauptspeicher zwischengespeichert, sodass jeder Thread vor der Ausführung von ALU-Operationen den aktualisierten Wert nach der ALU-Operation aus dem Hauptspeicher lesen muss, um in den Hauptspeicher zu schreiben. (Ein Schreibvorgang in eine flüchtige Variable v wird mit allen nachfolgenden Lesevorgängen von v durch einen beliebigen Thread synchronisiert.) Dies bedeutet, dass Änderungen an einer flüchtigen Variablen für andere Threads immer sichtbar sind.

Geben Sie hier die Bildbeschreibung ein

Hier ein , nonvoltaile variablewenn der Thread T1 den Wert in t1-Cache ändert, kann der Thread T2 nicht die geänderten Wert , bis t1 schreibt zugreifen, t2 aus dem Hauptspeicher für den letzten geänderten Wert lesen, die führen Data-Inconsistancy.

flüchtig kann nicht zwischengespeichert werden - Assembler

    +--------------+--------+-------------------------------------+
    |  Flag Name   |  Value | Interpretation                      |
    +--------------+--------+-------------------------------------+
    | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.|
    +--------------+--------+-------------------------------------+
    |ACC_TRANSIENT | 0x0080 | Declared transient; not written or  |
    |              |        | read by a persistent object manager.|
    +--------------+--------+-------------------------------------+

Shared Variables: Speicher, der von Threads gemeinsam genutzt werden kann, wird als gemeinsamer Speicher oder Heapspeicher bezeichnet. Alle Instanzfelder, statischen Felder und Array-Elemente werden im Heapspeicher gespeichert.

Synchronisation : Synchronisiert gilt für Methoden und Blöcke. Ermöglicht die gleichzeitige Ausführung von jeweils nur einem Thread für ein Objekt. Wenn t1 die Kontrolle übernimmt, müssen die verbleibenden Threads warten, bis die Kontrolle freigegeben wird.

Beispiel:

public class VolatileTest implements Runnable {

    private static final int MegaBytes = 10241024;

    private static final Object counterLock = new Object();
    private static int counter = 0;
    private static volatile int counter1 = 0;

    private volatile int counter2 = 0;
    private int counter3 = 0;

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            concurrentMethodWrong();
        }

    }

    void addInstanceVolatile() {
        synchronized (counterLock) {
            counter2 = counter2 + 1;
            System.out.println( Thread.currentThread().getName() +"\t\t « InstanceVolatile :: "+ counter2);
        }
    }

    public void concurrentMethodWrong() {
        counter = counter + 1;
        System.out.println( Thread.currentThread().getName() +" « Static :: "+ counter);
        sleepThread( 1/4 );

        counter1 = counter1 + 1;
        System.out.println( Thread.currentThread().getName() +"\t « StaticVolatile :: "+ counter1);
        sleepThread( 1/4 );

        addInstanceVolatile();
        sleepThread( 1/4 );

        counter3 = counter3 + 1;
        sleepThread( 1/4 );
        System.out.println( Thread.currentThread().getName() +"\t\t\t\t\t « Instance :: "+ counter3);
    }
    public static void main(String[] args) throws InterruptedException {
        Runtime runtime = Runtime.getRuntime();

        int availableProcessors = runtime.availableProcessors();
        System.out.println("availableProcessors :: "+availableProcessors);
        System.out.println("MAX JVM will attempt to use : "+ runtime.maxMemory() / MegaBytes );
        System.out.println("JVM totalMemory also equals to initial heap size of JVM : "+ runtime.totalMemory() / MegaBytes );
        System.out.println("Returns the amount of free memory in the JVM : "+ untime.freeMemory() / MegaBytes );
        System.out.println(" ===== ----- ===== ");

        VolatileTest volatileTest = new VolatileTest();
        Thread t1 = new Thread( volatileTest );
        t1.start();

        Thread t2 = new Thread( volatileTest );
        t2.start();

        Thread t3 = new Thread( volatileTest );
        t3.start();

        Thread t4 = new Thread( volatileTest );
        t4.start();

        Thread.sleep( 10 );;

        Thread optimizeation = new Thread() {
            @Override public void run() {
                System.out.println("Thread Start.");

                Integer appendingVal = volatileTest.counter2 + volatileTest.counter2 + volatileTest.counter2;

                System.out.println("End of Thread." + appendingVal);
            }
        };
        optimizeation.start();
    }

    public void sleepThread( long sec ) {
        try {
            Thread.sleep( sec * 1000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Statisch [ Class Field] vs flüchtig [Instance Field ] - Beide werden nicht von Threads zwischengespeichert

  • Statische Felder sind allen Threads gemeinsam und werden im Methodenbereich gespeichert. Statisch mit flüchtig ohne Verwendung. Das statische Feld kann nicht serialisiert werden.

  • Flüchtig, hauptsächlich verwendet mit Instanzvariablen, die im Heap-Bereich gespeichert werden. Die Hauptanwendung von volatile besteht darin, den aktualisierten Wert über alle Threads hinweg beizubehalten. Das flüchtige Feld der Instanz kann serialisiert werden .

@sehen

Yash
quelle
6

Wenn keepRunning nicht flüchtig war, sollte der Thread im Idealfall unbegrenzt weiterlaufen. Aber es hört nach ein paar Sekunden auf.

Wenn Sie mit einem einzelnen Prozessor arbeiten oder wenn Ihr System sehr ausgelastet ist, tauscht das Betriebssystem möglicherweise die Threads aus, was zu einer gewissen Cache-Ungültigkeit führt. Ein nicht zu haben volatilebedeutet nicht, dass das Gedächtnis nicht wird geteilt werden, aber die JVM nicht synchronisieren Speicher versucht , wenn es aus Performance - Gründen kann so dass der Speicher nicht aktualisiert werden kann.

Eine andere Sache zu beachten ist, dass System.out.println(...)synchronisiert wird, weil der BasiswertPrintStream synchronisiert, um überlappende Ausgaben zu stoppen. Sie erhalten also die Speichersynchronisation "kostenlos" im Haupt-Thread. Dies erklärt jedoch immer noch nicht, warum die Leseschleife die Aktualisierungen überhaupt sieht.

println(...)Unabhängig davon, ob die Leitungen ein- oder ausgehen, dreht sich Ihr Programm für mich unter Java6 auf einem MacBook Pro mit einem Intel i7.

Kann jemand volatile mit Beispiel erklären? Nicht mit der Theorie von JLS.

Ich finde dein Beispiel gut. Ich bin mir nicht sicher, warum es nicht funktioniert, wenn alle System.out.println(...)Anweisungen entfernt wurden. Für mich geht das.

Ist flüchtiger Ersatz für Synchronisation? Erreicht es Atomizität?

In Bezug auf die Speichersynchronisation volatilewerden die gleichen Speicherbarrieren wie bei einem synchronizedBlock ausgelöst, außer dass die volatileBarriere unidirektional oder bidirektional ist. volatileLesevorgänge werfen eine Lastsperre auf, während Schreibvorgänge eine Speicherbarriere aufwerfen. Ein synchronizedBlock ist eine bidirektionale Barriere mit zusätzlicher Mutex-Verriegelung.

In Bezug atomicityauf lautet die Antwort jedoch "es kommt darauf an". Wenn Sie einen Wert aus einem Feld lesen oder schreiben, erhalten Sie volatiledie richtige Atomizität. Das Inkrementieren eines volatileFeldes leidet jedoch unter der Einschränkung, dass ++es sich tatsächlich um drei Operationen handelt: Lesen, Inkrementieren, Schreiben. In diesem Fall oder in komplexeren Mutex-Fällen kann ein vollständiger synchronizedBlock erforderlich sein. AtomicIntegerlöst das ++Problem mit einer komplizierten Test-and-Set-Spin-Schleife.

Grau
quelle
Ich habe beide SOPln-Anweisungen kommentiert, aber sie werden nach einigen Sekunden immer noch gestoppt. Können Sie mir ein Beispiel zeigen, das wie erwartet funktionieren würde?
tmgr
Laufen Sie auf einem Einzelprozessorsystem @ tm99? Weil sich Ihr Programm für mich auf einem Macbook Pro Java6 für immer dreht.
Grau
Ich
laufe
2
"Jeder synchronisierte Block (oder jedes flüchtige Feld) bewirkt, dass der gesamte Speicher synchronisiert wird" - sind Sie sicher? Würden Sie einen JLS-Verweis darauf bereitstellen? Soweit ich mich erinnere, besteht die einzige Garantie darin, dass die vor dem Freigeben einer Sperre L1 vorgenommenen Änderungen am Speicher für Threads sichtbar sind, nachdem sie dieselbe Sperre L1 erhalten haben. Bei flüchtigen Stoffen sind alle Speichermodifikationen vor einem flüchtigen Schreibvorgang in F1 für einen Thread nach einem flüchtigen Lesevorgang desselben Feldes F1 sichtbar. Dies unterscheidet sich stark von der Aussage, dass der gesamte * Speicher synchronisiert ist. Es ist nicht so einfach wie ein Thread, der einen synchronisierten Block ausführt.
Bruno Reis
1
Wenn eine Speicherbarriere (mit synchronizedoder volatile) überschritten wird, besteht für den gesamten Speicher eine "Vorher-passiert" -Beziehung. Es gibt keine Garantie für die Reihenfolge der Sperren und die Synchronisierung, es sei denn, Sie sperren denselben Monitor, auf den Sie als @BrunoReis verwiesen werden. Wenn der println(...)Vorgang abgeschlossen ist, wird garantiert, dass das keepRunningFeld aktualisiert wird.
Grau
3

Wenn eine Variable ist volatile, wird garantiert, dass sie nicht zwischengespeichert wird und dass verschiedene Threads den aktualisierten Wert sehen. Eine Nichtmarkierung volatilegarantiert jedoch nicht das Gegenteil. volatilewar eines dieser Dinge, die in der JVM lange Zeit kaputt waren und immer noch nicht immer gut verstanden wurden.

Jeff Storey
quelle
In einem modernen Multiprozessor @Jeff ist Ihr letzter Kommentar etwas falsch / irreführend. Die JVM ist wirklich schlau , wenn es darum geht, den Wert nicht zu löschen, da dies ein Leistungseinbruch ist.
Grau
Wenn keepRunning von main auf false gesetzt ist, wird die Aktualisierung vom Thread weiterhin angezeigt, da die JVM beim Löschen des Werts klug ist. Dies ist jedoch nicht garantiert (siehe Kommentar von @Gray oben).
Jeff Storey
2

volatilewird nicht unbedingt riesige Änderungen verursachen, abhängig von der JVM und dem Compiler. In vielen (Rand-) Fällen kann es jedoch der Unterschied zwischen der Optimierung sein, die dazu führt, dass die Änderungen einer Variablen nicht bemerkt werden, anstatt dass sie korrekt geschrieben werden.

Grundsätzlich kann ein Optimierer wählen, nichtflüchtige Variablen in Register oder auf den Stapel zu setzen. Wenn ein anderer Thread sie im Heap oder in den Grundelementen der Klassen ändert, sucht der andere Thread im Stapel weiter danach und es ist veraltet.

volatile stellt sicher, dass solche Optimierungen nicht stattfinden und alle Lese- und Schreibvorgänge direkt auf den Heap oder einen anderen Ort erfolgen, an dem alle Threads sie sehen.

Nanofarad
quelle
2

Viele gute Beispiele, aber ich möchte nur hinzufügen, dass es eine Reihe von Szenarien gibt, in denen dies volatileerforderlich ist, sodass es kein konkretes Beispiel gibt, um sie zu regieren.

  1. Sie können verwenden volatile, um alle Threads zu zwingen, den neuesten Wert der Variablen aus dem Hauptspeicher abzurufen.
  2. Sie können synchronizationkritische Daten schützen
  3. Sie können LockAPI verwenden
  4. Sie können AtomicVariablen verwenden

Weitere Beispiele für flüchtige Java-Beispiele finden Sie hier .

Johnny
quelle
1

Nachfolgend finden Sie die Lösung:

Der Wert dieser Variablen wird niemals threadlokal zwischengespeichert: Alle Lese- und Schreibvorgänge werden direkt in den "Hauptspeicher" verschoben. Die flüchtige Kraft zwingt den Thread, die ursprüngliche Variable jedes Mal zu aktualisieren.

public class VolatileDemo {

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {

        ChangeMaker changeMaker = new ChangeMaker();
        changeMaker.start();

        ChangeListener changeListener = new ChangeListener();
        changeListener.start();

    }

    static class ChangeMaker extends Thread {

        @Override
        public void run() {
            while (MY_INT < 5){
                System.out.println("Incrementing MY_INT "+ ++MY_INT);
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException exception) {
                    exception.printStackTrace();
                }
            }
        }
    }

    static class ChangeListener extends Thread {

        int local_value = MY_INT;

        @Override
        public void run() {
            while ( MY_INT < 5){
                if( local_value!= MY_INT){
                    System.out.println("Got Change for MY_INT "+ MY_INT);
                    local_value = MY_INT;
                }
            }
        }
    }

}

Bitte verweisen Sie auf diesen Link http://java.dzone.com/articles/java-volatile-keyword-0 , um mehr Klarheit zu erhalten.

Azhaguvel A.
quelle
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Admdrew
Ja, du bist absolut richtig. Ich werde es hinzufügen. Vielen Dank für Ihren wertvollen Kommentar.
Azhaguvel A
1

Das flüchtige Schlüsselwort teilt der JVM mit, dass es möglicherweise von einem anderen Thread geändert wird. Jeder Thread hat einen eigenen Stapel und somit eine eigene Kopie der Variablen, auf die er zugreifen kann. Wenn ein Thread erstellt wird, kopiert er den Wert aller zugänglichen Variablen in seinen eigenen Speicher.

public class VolatileTest {
    private static final Logger LOGGER = MyLoggerFactory.getSimplestLogger();

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {
        new ChangeListener().start();
        new ChangeMaker().start();
    }

    static class ChangeListener extends Thread {
        @Override
        public void run() {
            int local_value = MY_INT;
            while ( local_value < 5){
                if( local_value!= MY_INT){
                    LOGGER.log(Level.INFO,"Got Change for MY_INT : {0}", MY_INT);
                     local_value= MY_INT;
                }
            }
        }
    }

    static class ChangeMaker extends Thread{
        @Override
        public void run() {

            int local_value = MY_INT;
            while (MY_INT <5){
                LOGGER.log(Level.INFO, "Incrementing MY_INT to {0}", local_value+1);
                MY_INT = ++local_value;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}

Probieren Sie dieses Beispiel mit und ohne flüchtig aus.

Adarsh ​​Mathew Thomas
quelle
0

Objekte, die als flüchtig deklariert sind, werden normalerweise verwendet, um Statusinformationen zwischen Threads zu kommunizieren. Um sicherzustellen, dass die CPU-Caches aktualisiert werden, dh synchron gehalten werden, wenn flüchtige Felder vorhanden sind, eine CPU-Anweisung, eine Speicherbarriere, die häufig als Membar oder bezeichnet wird Zaun, wird ausgegeben, um CPU-Caches mit einer Änderung des Werts eines flüchtigen Feldes zu aktualisieren.

Der flüchtige Modifikator teilt dem Compiler mit, dass die durch flüchtig modifizierte Variable von anderen Teilen Ihres Programms unerwartet geändert werden kann.

Die flüchtige Variable darf nur im Thread-Kontext verwendet werden. siehe das Beispiel hier

user2554822
quelle