Waarvoor is het vluchtige zoekwoord nuttig?

Vandaag kwam ik op het werk het volatiletrefwoord tegen in Java. Omdat ik er niet erg bekend mee was, vond ik deze uitleg.

Gezien de details waarin dat artikel het betreffende zoekwoord uitlegt, gebruikt u het dan ooit of ziet u ooit een geval waarin u dit zoekwoord op de juiste manier zou kunnen gebruiken?


Antwoord 1, autoriteit 100%

volatileheeft semantiek voor geheugenzichtbaarheid. In principe wordt de waarde van een volatile-veld zichtbaar voor alle lezers (met name andere threads) nadat een schrijfbewerking erop is voltooid. Zonder volatilezouden lezers een niet-geüpdatete waarde kunnen zien.

Om je vraag te beantwoorden: Ja, ik gebruik een volatilevariabele om te bepalen of bepaalde code een lus voortzet. De lus test de volatilewaarde en gaat verder als deze trueis. De voorwaarde kan worden ingesteld op falsedoor een “stop”-methode aan te roepen. De lus ziet falseen eindigt wanneer de waarde wordt getest nadat de stop-methode de uitvoering heeft voltooid.

Het boek “Java Concurrency in Practice“, dat ik ten zeerste aanbeveel, geeft een goede uitleg van volatile. Dit boek is geschreven door dezelfde persoon die het IBM-artikel heeft geschreven waarnaar in de vraag wordt verwezen (in feite citeert hij zijn boek onderaan dat artikel). Mijn gebruik van volatileis wat zijn artikel de “patroon 1-statusvlag” noemt.

Als je meer wilt weten over hoe volatilewerkt onder de motorkap, lees op het Java-geheugenmodel. Als je verder wilt gaan dan dat niveau, bekijk dan eens een goed computerarchitectuurboek zoals Hennessy & Pattersonen lees over cachecoherentie en cacheconsistentie.


Antwoord 2, autoriteit 24%

“… de vluchtige modifier garandeert dat elke thread die een veld leest de meest recent geschreven waarde zal zien.”– Josh Bloch

Als je overweegt om volatilete gebruiken, lees dan het pakket java.util.concurrentdie zich bezighoudt met atomair gedrag.

Het Wikipedia-bericht op een Singleton Patternvertoont vluchtig gebruik.


Antwoord 3, autoriteit 19%

Belangrijk punt over volatile:

  1. Synchronisatie in Java is mogelijk door gebruik te maken van Java-trefwoorden synchronizeden volatileen sloten.
  2. In Java kunnen we geen synchronizedvariabele hebben. Het gebruik van synchronizedtrefwoord met een variabele is illegaal en zal resulteren in een compilatiefout. In plaats van de synchronizedvariabele in Java te gebruiken, kunt u de java volatilevariabele gebruiken, die JVM-threads zal instrueren om de waarde van de volatilevariabele te lezen uit het hoofdgeheugen en niet lokaal in de cache plaatsen.
  3. Als een variabele niet tussen meerdere threads wordt gedeeld, is het niet nodig om het trefwoord volatilete gebruiken.

bron

Voorbeeld van gebruik van volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

We maken een instantie lui op het moment dat het eerste verzoek komt.

Als we de variabele _instanceniet volatilemaken, kan de thread die de instantie van Singletonaanmaakt niet communiceren met de andere draad. Dus als Thread A een Singleton-instantie aan het maken is en net na het maken, corrumpeert de CPU, enz., zullen alle andere threads de waarde van _instanceniet als niet null zien en zullen ze geloven dat het nog steeds null is toegewezen .

Waarom gebeurt dit? Omdat de threads van de lezer geen vergrendeling uitvoeren en totdat de thread van de schrijver uit een gesynchroniseerd blok komt, wordt het geheugen niet gesynchroniseerd en wordt de waarde van _instanceniet bijgewerkt in het hoofdgeheugen. Met het vluchtige sleutelwoord in Java wordt dit afgehandeld door Java zelf en dergelijke updates zullen zichtbaar zijn voor alle lezersthreads.

Conclusie: volatiletrefwoord wordt ook gebruikt om de inhoud van het geheugen tussen threads te communiceren.

Voorbeeld van gebruik zonder vluchtig:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

De bovenstaande code is niet thread-safe. Hoewel het de waarde van de instantie opnieuw controleert binnen het gesynchroniseerde blok (om prestatieredenen), kan de JIT-compiler de bytecode zo herschikken dat de verwijzing naar de instantie wordt ingesteld voordat de constructor de uitvoering heeft voltooid. Dit betekent dat de methode getInstance() een object retourneert dat mogelijk niet volledig is geïnitialiseerd. Om de code thread-safe te maken, kan het sleutelwoord vluchtig worden gebruikt sinds Java 5 voor de instantievariabele. Variabelen die zijn gemarkeerd als vluchtig, worden alleen zichtbaar voor andere threads zodra de constructor van het object de uitvoering volledig heeft voltooid.
Bron

voer hier de afbeeldingsbeschrijving in

volatilegebruik in Java:

De faalsnelle iterators worden meestalgeïmplementeerd met behulp van een volatile-teller op het lijstobject.

  • Als de lijst is bijgewerkt, wordt de teller verhoogd.
  • Wanneer een Iteratorwordt gemaakt, wordt de huidige waarde van de teller ingebed in het Iterator-object.
  • Wanneer een Iterator-bewerking wordt uitgevoerd, vergelijkt de methode de twee tellerwaarden en genereert een ConcurrentModificationExceptionals ze verschillend zijn.

De implementatie van faalveilige iterators is doorgaans licht van gewicht. Ze vertrouwen doorgaans op eigenschappen van de gegevensstructuren van de specifieke lijstimplementatie. Er is geen algemeen patroon.


Antwoord 4, autoriteit 8%

volatileis erg handig om discussies te stoppen.

Niet dat je je eigen threads zou moeten schrijven, Java 1.6 heeft veel mooie threadpools. Maar als je zeker weet dat je een thread nodig hebt, moet je weten hoe je deze kunt stoppen.

Het patroon dat ik gebruik voor garen is:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

In het bovenstaande codesegment is de thread die closeleest in de while-lus anders dan de thread die close()aanroept. Zonder vluchtig, ziet de thread die de lus uitvoert misschien nooit de verandering om te sluiten.

Merk op dat synchronisatie niet nodig is


Antwoord 5, autoriteit 4%

Een variabele gedeclareerd met volatiletrefwoord, heeft twee belangrijke eigenschappen die hem speciaal maken.

  1. Als we een vluchtige variabele hebben, kan deze door geen enkele thread in het cachegeheugen van de computer (microprocessor) worden opgeslagen. Toegang vond altijd plaats vanuit het hoofdgeheugen.

  2. Als er een schrijfbewerkingplaatsvindt op een vluchtige variabele en plotseling een leesbewerkingwordt gevraagd, is het gegarandeerd dat de schrijfbewerking voltooid is voorafgaand aan de leesbewerking.

Twee bovenstaande eigenschappen leiden af dat

  • Alle threads die een vluchtige variabele lezen, zullen zeker de laatste waarde lezen. Omdat geen enkele waarde in de cache het kan vervuilen. En ook het leesverzoek wordt pas ingewilligd nadat de huidige schrijfbewerking is voltooid.

En aan de andere kant,

  • Als we de #2die ik heb genoemd verder onderzoeken, kunnen we zien dat het volatile-zoekwoord een ideale manier is om een gedeelde variabele met ‘n’ aantal lezersthreads en slechts één schrijverthreadom toegang te krijgen. Zodra we het volatilezoekwoord hebben toegevoegd, is het klaar. Geen andere overhead over draadveiligheid.

Omgekeerd,

We kunnenniet alleen het volatilezoekwoord gebruiken om te voldoen aan een gedeelde variabele die meer dan één schrijversthread die er toegang toe heeft.


Antwoord 6, autoriteit 4%

Een veelvoorkomend voorbeeld van het gebruik van volatileis het gebruik van een volatile booleanvariabele als vlag om een thread te beëindigen. Als u een thread bent gestart en u wilt deze veilig kunnen onderbreken vanuit een andere thread, dan kunt u de thread periodiek een vlag laten controleren. Om het te stoppen, stelt u de vlag in op waar. Door de vlag volatilete maken, kunt u ervoor zorgen dat de thread die deze controleert, ziet dat deze de volgende keer dat deze wordt gecontroleerd is ingesteld, zonder dat u zelfs een synchronized-blok hoeft te gebruiken .


Antwoord 7, autoriteit 2%

Niemand heeft melding gemaakt van de behandeling van lees- en schrijfbewerkingen voor het type lange en dubbele variabele. Lezen en schrijven zijn atomaire bewerkingen voor referentievariabelen en voor de meeste primitieve variabelen, behalve voor lange en dubbele variabeletypen, die het vluchtige sleutelwoord moeten gebruiken om atomaire bewerkingen te zijn. @link


Antwoord 8, autoriteit 2%

Ja, vluchtig moet worden gebruikt wanneer u wilt dat een veranderlijke variabele toegankelijk is voor meerdere threads. Het is niet erg gebruikelijk, omdat u doorgaans meer dan een enkele atomaire bewerking moet uitvoeren (controleer bijvoorbeeld de variabelestatus voordat u deze wijzigt), in welk geval u in plaats daarvan een gesynchroniseerd blok zou gebruiken.


Antwoord 9

Naar mijn mening zijn er twee andere belangrijke scenario’s dan het stoppen van threads waarin vluchtig zoekwoord wordt gebruikt:

  1. Dubbel gecontroleerd vergrendelingsmechanisme. Vaak gebruikt in Singleton-ontwerp
    patroon. Hierin moet het singleton object vluchtig worden verklaard.
  2. Spurious Wakeups. Discussie kan soms ontwaken uit wachtoproep, zelfs als er geen meldingsoproep is gedaan. Dit gedrag wordt onechte wake-up genoemd. Dit kan worden tegengegaan door een voorwaardelijke variabele (booleaanse vlag) te gebruiken. Plaats de wait()-aanroep in een while-lus zolang de vlag waar is. Dus als de thread ontwaakt uit een wachtoproep vanwege andere redenen dan Notify / NotifyAll, dan komt de vlag nog steeds waar en dus wachten de oproepen opnieuw. Stel deze vlag in op waar voordat u melding aanroept. In dit geval wordt de booleaanse vlag als vluchtig verklaard.

Antwoord 10

volatile=> synchronized[Over]

volatilezegt voor een programmeur dat de waarde altijd up-to-date zal zijn. Het probleem is dat de waarde kan worden opgeslagen op verschillende soorten hardwaregeheugen. Het kunnen bijvoorbeeld CPU-registers, CPU-cache, RAM zijn… СPU-registers en CPU-cache behoren tot de CPU en kunnen geen gegevens delen in tegenstelling tot RAM die in een multithreading-omgeving wordt gered

voer hier de afbeeldingsbeschrijving in

volatilezoekwoord zegt dat een variabele gelezen en geschrevenvan/naar het RAM-geheugen directzal worden. Het heeft enige rekenvoetafdruk

Java 5uitgebreid volatiledoor ondersteuning van happens-before[Over]

Er wordt naar een vluchtig veld geschreven, vóór elke volgende lezing van dat veld.

Read is after write

volatilezoekwoord geneest nieteen race conditionsituatie wanneer meerdere threads tegelijkertijd enkele waarden kunnen schrijven. Het antwoord is synchronizedzoekwoord[Over]

Als gevolg hiervan is het alleen veilig wanneer éénthread schrijften anderen alleen de volatilewaarde

lezen


Antwoord 11

U moet het trefwoord ‘vluchtig’ of ‘gesynchroniseerd’ en alle andere hulpmiddelen en technieken voor gelijktijdigheidscontrole gebruiken die u mogelijk tot uw beschikking heeft als u een toepassing met meerdere threads ontwikkelt. Een voorbeeld van een dergelijke toepassing zijn desktop-apps.

Als u een toepassing ontwikkelt die zou worden geïmplementeerd op de toepassingsserver (Tomcat, JBoss AS, Glassfish, enz.), hoeft u zelf geen gelijktijdigheidscontrole uit te voeren, aangezien dit al door de toepassingsserver wordt gedaan. In feite, als ik het me goed herinner, verbiedt de Java EE-standaard elke concurrency-controle in servlets en EJB’s, omdat het deel uitmaakt van de ‘infrastructuur’-laag waarvan je verondersteld wordt te worden verlost van het hanteren ervan. U voert alleen gelijktijdigheidscontrole uit in een dergelijke app als u singleton-objecten implementeert. Dit is zelfs al verholpen als je je componenten breit met frameworks zoals Spring.

Dus in de meeste gevallen van Java-ontwikkeling, waarbij de applicatie een webapplicatie is en het IoC-framework zoals Spring of EJB gebruikt, hoeft u ‘vluchtig’ niet te gebruiken.


Antwoord 12

volatilegarandeert alleen dat alle threads, zelfs zichzelf, toenemen. Bijvoorbeeld: een teller ziet tegelijkertijd hetzelfde gezicht van de variabele. Het wordt niet gebruikt in plaats van gesynchroniseerd of atomair of andere dingen, het maakt de uitlezingen volledig gesynchroniseerd. Vergelijk het alstublieft niet met andere java-zoekwoorden. Zoals het onderstaande voorbeeld laat zien, zijn vluchtige variabele operaties ook atomair, ze mislukken of slagen meteen.

package io.netty.example.telnet;
import java.util.ArrayList;
import java.util.List;
public class Main {
    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{
        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }
        for (Thread thread : list) {
            thread.start();
        }
        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

Zelfs als je volatiel of niet zet, zullen de resultaten altijd verschillen. Maar als u AtomicInteger gebruikt, zoals hieronder, zijn de resultaten altijd hetzelfde. Dit is ook hetzelfde met gesynchroniseerd.

   package io.netty.example.telnet;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    public class Main {
        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{
            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }
            for (Thread thread : list) {
                thread.start();
            }
            Thread.sleep(20000);
            System.out.println(a.get());
        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

Antwoord 13

Ja, ik gebruik het vrij veel – het kan erg handig zijn voor multi-threaded code. Het artikel waar je naar verwijst is een goede. Er zijn echter twee belangrijke dingen om in gedachten te houden:

  1. Gebruik vluchtig alleen als je
    volledig begrijpen wat het doet
    en hoe het verschilt van gesynchroniseerd.
    In veel situaties verschijnt vluchtig,
    aan de oppervlakte, om een eenvoudiger meer te zijn
    performant alternatief voor
    gesynchroniseerd, wanneer vaak een betere
    begrip van vluchtig zou maken
    duidelijk dat gesynchroniseerd de enige is
    optie die zou werken.
  2. vluchtig werkt niet echt in een
    veel oudere JVM’s, hoewel
    gesynchroniseerd doet. Ik herinner me dat ik een document zag dat verwees naar de verschillende ondersteuningsniveaus in verschillende JVM’s, maar helaas kan ik het nu niet vinden. Kijk er zeker naar als je Java pre 1.5 gebruikt of als je geen controle hebt over de JVM’s waarop je programma zal draaien.

Antwoord 14

Elke thread die toegang heeft tot een vluchtig veld, leest de huidige waarde voordat hij verder gaat, in plaats van (mogelijk) een waarde in de cache te gebruiken.

Alleen lidvariabele kan vluchtig of tijdelijk zijn.


Antwoord 15

Stel dat een thread de waarde van een gedeelde variabele wijzigt, als je geen volatile-modifier voor die variabele hebt gebruikt. Wanneer andere threads de waarde van deze variabele willen lezen, zien ze de bijgewerkte waarde niet omdat ze de waarde van de variabele uit de CPU-cache lezen in plaats van uit het RAM-geheugen. Dit probleem staat ook bekend als Visibility Problem.

Door de gedeelde variabele volatilete declareren, worden alle schrijfacties naar de tellervariabele onmiddellijk teruggeschreven naar het hoofdgeheugen. Ook worden alle uitlezingen van de tellervariabele direct uit het hoofdgeheugen gelezen.

public class SharedObject {
    public volatile int sharedVariable = 0;
}

Met niet-vluchtige variabelen zijn er geen garanties over wanneer de Java Virtual Machine (JVM) gegevens uit het hoofdgeheugen in de CPU-caches leest of gegevens uit de CPU-caches naar het hoofdgeheugen schrijft. Dit kan verschillende problemen veroorzaken die ik in de volgende paragrafen zal uitleggen.


Voorbeeld:

Stel je een situatie voor waarin twee of meer threads toegang hebben tot een gedeeld object dat een tellervariabele bevat die als volgt is gedeclareerd:

public class SharedObject {
    public int counter = 0;
}

Stel je ook voor dat alleen Thread 1 de tellervariabele verhoogt, maar dat zowel Thread 1 als Thread 2 de tellervariabele van tijd tot tijd kunnen lezen.

Als de tellervariabele niet vluchtig wordt verklaard, is er geen garantie dat de waarde van de tellervariabele vanuit de CPU-cache terug naar het hoofdgeheugen wordt geschreven. Dit betekent dat de waarde van de tellervariabele in de CPU-cache niet hetzelfde kan zijn als in het hoofdgeheugen. Deze situatie wordt hier geïllustreerd:

vluchtig

Het probleem met threads die de laatste waarde van een variabele niet zien omdat deze nog niet door een andere thread naar het hoofdgeheugen is teruggeschreven, wordt een “zichtbaarheidsprobleem” genoemd. De updates van een thread zijn niet zichtbaar voor andere threads.


Antwoord 16

Absoluut, ja. (En niet alleen in Java, maar ook in C#.) Er zijn momenten waarop u een waarde moet krijgen of instellen die gegarandeerd een atomaire bewerking is op uw gegeven platform, bijvoorbeeld een int of boolean, maar niet vereist de overhead van draadvergrendeling. Met het vluchtige sleutelwoord kunt u ervoor zorgen dat wanneer u de waarde leest, u de huidigewaarde krijgt en niet een in de cache opgeslagen waarde die zojuist verouderd is gemaakt door een schrijven op een andere thread.


Antwoord 17

Er zijn twee verschillende toepassingen van vluchtig zoekwoord.

  1. Voorkomt dat JVM waarden uit het register leest (veronderstel als cache) en dwingt de waarde ervan uit het geheugen te lezen.
  2. Verkleint het risico op fouten met de consistentie van het geheugen.

Voorkomt dat JVM waarden in het register leest en dwingt zijn
waarde die uit het geheugen moet worden gelezen.

Een bezet-vlagwordt gebruikt om te voorkomen dat een gesprek doorgaat terwijl het apparaat bezet is en de vlag niet wordt beschermd door een slot:

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

De testthread gaat verder wanneer een andere thread de bezet-vlaguitschakelt:

busy = 0;

Omdat echter vaak in de testthread naar bezet is, kan de JVM de test optimaliseren door de waarde van busy in een register te plaatsen en vervolgens de inhoud van het register te testen zonder de waarde van busy in het geheugen te lezen voor elke test. De testthread zou nooit bezet veranderen en de andere thread zou alleen de waarde van busy in het geheugen veranderen, wat resulteert in een impasse. Door de bezet-vlagals vluchtig te verklaren, wordt de waarde ervan vóór elke test gelezen.

Vermindert het risico op fouten in de geheugenconsistentie.

Het gebruik van vluchtige variabelen vermindert het risico op geheugenconsistentiefouten, omdat elke schrijfactie naar een vluchtige variabele een
“happens-before”relatie met daaropvolgende reads van dezelfde variabele. Dit betekent dat wijzigingen aan een vluchtige variabele altijd zichtbaar zijn voor andere threads.

De techniek van lezen en schrijven zonder fouten in de geheugenconsistentie wordt atomaire actiegenoemd.

Een atomaire actie is een actie die in één keer plaatsvindt. Een atomaire actie kan niet halverwege stoppen: het gebeurt volledig, of het gebeurt helemaal niet. Er zijn geen bijwerkingen van een atomaire actie zichtbaar totdat de actie is voltooid.

Hieronder staan acties die u kunt specificeren die atomair zijn:

  • Lezen en schrijven zijn atomair voor referentievariabelen en voor de meeste
    primitieve variabelen (alle typen behalve long en double).
  • Lezen en schrijven zijn atomair voor alle variabelen die als vluchtigzijn verklaard
    (inclusief lange en dubbele variabelen).

Proost!


Antwoord 18

Hoewel ik veel goede theoretische verklaringen zie in de hier genoemde antwoorden, voeg ik hier een praktisch voorbeeld met uitleg toe:

1.

CODE UITVOEREN ZONDER VLUCHTIG GEBRUIK

public class VisibilityDemonstration {
private static int sCount = 0;
public static void main(String[] args) {
    new Consumer().start();
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        return;
    }
    new Producer().start();
}
static class Consumer extends Thread {
    @Override
    public void run() {
        int localValue = -1;
        while (true) {
            if (localValue != sCount) {
                System.out.println("Consumer: detected count change " + sCount);
                localValue = sCount;
            }
            if (sCount >= 5) {
                break;
            }
        }
        System.out.println("Consumer: terminating");
    }
}
static class Producer extends Thread {
    @Override
    public void run() {
        while (sCount < 5) {
            int localValue = sCount;
            localValue++;
            System.out.println("Producer: incrementing count to " + localValue);
            sCount = localValue;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                return;
            }
        }
        System.out.println("Producer: terminating");
    }
}
}

In de bovenstaande code zijn er twee threads: Producer en Consument.

De producerthread herhaalt de lus 5 keer (met een slaapstand van 1000 milliseconde of 1 sec) ertussen. In elke iteratie verhoogt de producerthread de waarde van sCount-variabele met 1. Dus de producer verandert de waarde van sCount van 0 in 5 in alle iteraties

De consumententhread bevindt zich in een constante lus en wordt afgedrukt wanneer de waarde van sCount verandert totdat de waarde 5 bereikt waar deze eindigt.

Beide lussen worden tegelijkertijd gestart. Dus zowel de producent als de consument moeten de waarde van sCount 5 keer afdrukken.

UITVOER

Consumer: detected count change 0
Producer: incrementing count to 1
Producer: incrementing count to 2
Producer: incrementing count to 3
Producer: incrementing count to 4
Producer: incrementing count to 5
Producer: terminating

ANALYSE

In het bovenstaande programma, wanneer de producentthread de waarde van sCount bijwerkt, wordt de waarde van de variabele in het hoofdgeheugen bijgewerkt (geheugen van waaruit elke thread in eerste instantie de waarde van de variabele gaat lezen). Maar de consumententhread leest de waarde van sCount alleen de eerste keer uit dit hoofdgeheugen en slaat vervolgens de waarde van die variabele op in zijn eigen geheugen. Dus zelfs als de waarde van de originele sCount in het hoofdgeheugen is bijgewerkt door de producentthread, leest de consumerthread uit de cachewaarde die niet is bijgewerkt. Dit wordt ZICHTBAARHEIDSPROBLEEMgenoemd.

2.

CODE UITVOEREN MET VLUCHTIG GEBRUIK

Vervang in de bovenstaande code de coderegel waarin sCount wordt gedeclareerd door het volgende:

private volatile  static int sCount = 0;

UITVOER

Consumer: detected count change 0
Producer: incrementing count to 1
Consumer: detected count change 1
Producer: incrementing count to 2
Consumer: detected count change 2
Producer: incrementing count to 3
Consumer: detected count change 3
Producer: incrementing count to 4
Consumer: detected count change 4
Producer: incrementing count to 5
Consumer: detected count change 5
Consumer: terminating
Producer: terminating

ANALYSE

Als we een variabele vluchtig declareren, betekent dit dat alle lees- en schrijfbewerkingen naar deze variabele of van deze variabele direct naar het hoofdgeheugen gaan. De waarden van deze variabelen worden nooit in de cache opgeslagen.

Omdat de waarde van de sCount-variabele nooit door een thread wordt gecached, leest de consument altijd de oorspronkelijke waarde van sCount uit het hoofdgeheugen (waar deze wordt bijgewerkt door de producerthread). Dus in dit geval is de uitvoer correct waarbij beide threads de verschillende waarden van sCount 5 keer afdrukken.

Op deze manier lost het vluchtige zoekwoord het ZICHTBAARHEIDSPROBLEEMop.


Antwoord 19

Vluchtig doet het volgende.

1> Lezen en schrijven van vluchtige variabelen door verschillende threads zijn altijd uit het geheugen, niet uit de eigen cache of cpu-register van de thread. Elke thread behandelt dus altijd de laatste waarde.
2> Wanneer 2 verschillende threads werken met dezelfde instantie of statische variabelen in de heap, kan men de acties van anderen als niet in orde zien. Zie de blog van Jeremy Manson hierover. Maar vluchtig helpt hier.

Het volgen van volledig actieve code laat zien hoe een aantal threads kan worden uitgevoerd in een vooraf gedefinieerde volgorde en uitvoer kan afdrukken zonder gesynchroniseerd trefwoord te gebruiken.

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

Om dit te bereiken kunnen we de volgende volwaardige hardloopcode gebruiken.

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
                    }
                }
            }
        }
    }
}

De volgende github-link heeft een leesmij, die de juiste uitleg geeft.
https://github.com/sankar4git/volatile_thread_ordering


Antwoord 20

Van Oracle-documentatie pagina, de noodzaak van vluchtige variabele ontstaat om problemen met geheugenconsistentie op te lossen:

Het gebruik van vluchtige variabelen vermindert het risico op fouten in de geheugenconsistentie, omdat elke schrijfactie naar een vluchtige variabele een happening-before-relatie tot stand brengt met daaropvolgende leesbewerkingen van diezelfde variabele.

Dit betekent dat wijzigingen in een volatilevariabele altijd zichtbaar zijn voor andere threads. Het betekent ook dat wanneer een thread een vluchtige variabele leest, deze niet alleen de laatste wijziging in de volatileziet, maar ook de bijwerkingen van de code die tot de wijziging leidde.

Zoals uitgelegd in het antwoord van Peter Parker, kan bij afwezigheid van een volatile-modifier de stack van elke thread zijn eigen kopie van de variabele hebben. Door de variabele als volatilete maken, zijn problemen met de geheugenconsistentie opgelost.

Bekijk de jenkov-tutorialpagina voor een beter begrip.

Bekijk de gerelateerde SE-vraag voor meer details over vluchtige & use cases om vluchtig te gebruiken:

Verschil tussen vluchtig en gesynchroniseerd in Java

Eén praktisch gebruiksvoorbeeld:

Je hebt veel threads die de huidige tijd in een bepaald formaat moeten afdrukken, bijvoorbeeld: java.text.SimpleDateFormat("HH-mm-ss"). Yon kan één klasse hebben, die de huidige tijd omzet in SimpleDateFormaten de variabele elke seconde bijwerkt. Alle andere threads kunnen deze vluchtige variabele eenvoudig gebruiken om de huidige tijd in logbestanden af te drukken.


Antwoord 21

Vluchtige variabelen zijn lichtgewicht synchronisatie. Wanneer zichtbaarheid van de nieuwste gegevens tussen alle threads een vereiste is en de atomiciteit in het gedrang kan komen, moeten in dergelijke situaties vluchtige variabelen de voorkeur krijgen. Lezen op vluchtige variabelen retourneren altijd de meest recente schrijfbewerkingen die door een thread zijn gedaan, omdat ze niet in de cache zijn opgeslagen in registers of in caches waar andere processors niet kunnen zien. Vluchtig is Lock-Free. Ik gebruik vluchtig, wanneer het scenario voldoet aan de criteria zoals hierboven vermeld.


Antwoord 22

vluchtige variabele wordt in principe gebruikt voor onmiddellijke update (flush) in de gedeelde hoofdcacheregel zodra deze is bijgewerkt, zodat wijzigingen onmiddellijk worden doorgevoerd in alle werkthreads.


Antwoord 23

De vluchtige sleutel, wanneer gebruikt met een variabele, zorgt ervoor dat threads die deze variabele lezen dezelfde waarde zullen zien. Als u nu meerdere threads hebt die een variabele lezen en schrijven, is het niet voldoende om de variabele vluchtig te maken en worden de gegevens beschadigd. Afbeeldingsthreads hebben dezelfde waarde gelezen, maar elk heeft enkele wijzigingen uitgevoerd (bijvoorbeeld een teller verhoogd), bij het terugschrijven naar het geheugen wordt de gegevensintegriteit geschonden. Daarom is het nodig om de variabele gesynchroniseerd te maken (verschillende manieren zijn mogelijk)

Als de wijzigingen door 1 thread worden gedaan en de anderen hoeven alleen deze waarde te lezen, is de vluchtige versie geschikt.


Antwoord 24

Hieronder is een zeer eenvoudige code om de vereiste van volatilevoor variabele aan te tonen die wordt gebruikt om de thread-uitvoering van een andere thread te besturen (dit is een scenario waarin volatileis vereist).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();
        Thread.sleep(500);
        long stoppedOn = System.nanoTime();
        task.stop(); // -----> do this to stop the thread
        System.out.println("Stopping on: " + stoppedOn);
    }
}
class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;
    public void stop() {
        state = false;
    } 
    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

Als volatileniet wordt gebruikt:ziet u nooit het bericht ‘Gestopt op: xxx‘, zelfs niet na ‘Stoppen op : xxx‘, en het programma blijft draaien.

Stopping on: 1895303906650500

Als volatilewordt gebruikt:zie je meteen de tekst ‘Gestopt op: xxx‘.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Demo: https://repl.it/repls/SilverAgonizingObjectcode

Other episodes