volatile is een modifier voor een variabele die ervoor zorgt dat wijzigingen aan deze variabele zichtbaar zijn voor alle threads. Als een variabele als volatile is gedeclareerd, dan vindt het lezen en schrijven rechtstreeks uit het hoofdgeheugen plaats, waarbij de lokale caches van de threads worden overgeslagen. Dit voorkomt dat waarden lokaal in de thread worden gecached.
synchronized is een sleutelwoord dat niet alleen zichtbaarheid garandeert, maar ook mutual exclusion: slechts één thread kan op een bepaald moment een gesynchroniseerd blok uitvoeren voor één object.
Gebruik volatile variabelen voor eenvoudige vlaggen en tellers, als:
Voorbeeld van het gebruik van volatile:
class Flag { private volatile boolean running = true; public void stop() { running = false; } public void loop() { while (running) { // ... } } }
Kan volatile worden gebruikt om de atomiciteit van de incrementele variabele te waarborgen?
Antwoord:
Nee, volatile garandeert niet de atomiciteit van operaties. Bijvoorbeeld, counter++ is geen atomische operatie, zelfs als counter als volatile is gedeclareerd, omdat de operatie lezen, wijzigen en schrijven omvat (meerdere acties), die door een andere thread kunnen worden onderbroken. Atomiciteit wordt verzekerd met behulp van synchronized of classes uit het pakket java.util.concurrent.atomic.
Voorbeeld:
class Counter { private volatile int count = 0; public void increment() { count++; // NIET atomische operatie! } }
Verhaal
In het project van een online winkel werd de vlag voor het beëindigen van de orderverwerkingsthread gerealiseerd zonder volatile. Als gevolg hiervan bleef een van de threads "hangen" in een oneindige lus, omdat deze de wijziging van de variabele, aangebracht door een andere thread, niet zag. De diagnose duurde enkele dagen.
Verhaal
In het financiële systeem gebruikte de ontwikkelaar een volatile int voor de teller van operaties. Bij piekbelastingen begon het aantal operaties te "verliezen". De reden was het verlies van atomiciteit bij complexe incrementele operaties.
Verhaal
Ontwikkelaars verwarden volatile en synchronized en probeerden volatile te gebruiken om wederzijdse uitsluiting van toegang tot kritieke secties te waarborgen, wat leidde tot data race en moeilijk herkenbare bugs in de multithreaded applicatie.