Geschiedenis van de kwestie:
Toen Java net verscheen, was er geen formele beschrijving van hoe geheugen en threads met elkaar interactie hebben. Hierdoor konden dezelfde programma's zich verschillend gedragen op verschillende JVM's, wat leidde tot moeilijk te traceren bugs. In 2004 werd het Java Memory Model (JMM) aan de specificatie van Java toegevoegd om strikt te definiëren hoe threads updates van variabelen zien.
Probleem:
Zonder een duidelijk geheugenmodel kunnen schrijf- en leewerkzaamheden door de compiler of processor door elkaar worden gehaald, wat leidt tot onverwacht gedrag bij toegang tot gedeelde variabelen vanuit verschillende threads. Dit kan racecondities, zichtbaarheidproblemen en moeilijk te debuggen synchronisatiefouten veroorzaken.
Oplossing:
JMM definieert regels: wanneer de wijzigingen aangebracht door één thread zichtbaar worden voor andere. Het stelt concepten in zoals happens-before, synchronisatie (synchronized, volatile, final), interne vergrendelingen en beperkt de herschikking van instructies in een multithreadomgeving.
Codevoorbeeld:
class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int get() { return count; } }
Belangrijke kenmerken:
Waarom maakt volatile operaties niet atomair, maar zorgt het alleen voor zichtbaarheid?
Volatile garandeert alleen zichtbaarheid van wijzigingen tussen threads, maar niet de atomiciteit van een wijziging. Bijvoorbeeld, het verhogen van een volatile variabele is geen atomaire operatie, omdat de actie zelf bestaat uit lezen, veranderen en schrijven.
Codevoorbeeld:
volatile int count = 0; count++; // Niet atomair!
Kan een synchronized blok zichtbaarheid van wijzigingen buiten zijn grenzen garanderen?
Ja. Na het verlaten van een synchronized blok zijn alle wijzigingen die binnen dat blok zijn aangebracht zichtbaar voor andere threads die in dat blok op hetzelfde object-monitor binnenkomen.
Wanneer worden wijzigingen in final-velden zichtbaar voor andere threads?
Final-velden garanderen een correcte publicatie alleen als het object volledig is geconstrueerd voordat verwijzingen naar dat object aan andere threads worden gegeven, bijvoorbeeld via immutable objecten. Anders kan zichtbaarheid van een niet-geïnitieerd staat optreden.
Negatief geval
Een ontwikkelaar besloot het aantal bezoeken aan de website te verhogen met behulp van een eenvoudige volatile increment vanuit meerdere threads.
Voordelen:
Nadelen:
Positief geval
AtomicInteger werd gebruikt voor de teller of synchronized methoden.
Voordelen:
Nadelen: