Memory Model
JMM, happens-before reláció, láthatóság és atomicitás
Java Memory Model
A JMM (JSR-133) meghatározza a szabályokat, amelyek szerint a szálak a közös memórián keresztül kommunikálnak — pontosan megadva, hogy az egyik szál által végzett írás mikor válik láthatóvá egy másik szál olvasásai számára.
1. Definíció
Mi ez?
A Java Memory Model (JMM), amelyet a JSR-133 specifikáció szabványosított és a Java 5-ben vezettek be, az a formális leírás, amely meghatározza, hogyan lépnek kölcsönhatásba a szálak a megosztott memórián keresztül. Egy alapvető kérdést válaszol meg:
"Mikor garantált, hogy az A szál által írt értéket a B szál látni fogja?"
Jól definiált memory model nélkül a JVM, a fordító (JIT) és a CPU szabadon átrendezheti az utasításokat és gyorsítótárazhatja az értékeket — a single-threaded teljesítmény javítása érdekében, de a multi-threaded helyesség kárára.
Miért létezik?
A modern hardver és a fordítók számos, forráskód szinten láthatatlan optimalizációt alkalmaznak:
- CPU cache-ek: minden magnak saját L1/L2 gyorsítótára van; az írások nem feltétlenül kerülnek azonnal a főmemóriába.
- Store buffer-ek: a CPU által kibocsátott írás a store buffer-ben maradhat, mielőtt globálisan láthatóvá válna.
- Utasítás-átrendezés: a fordítók (JIT) és az out-of-order CPU-k eltérő sorrendben hajthatják végre az utasításokat, amennyiben az eredmény single-threaded szempontból helyes.
Ezek az optimalizációk biztonságosak egyszálas környezetben. Többszálasban azonban ahhoz vezethetnek, hogy az egyik thread elavult vagy részleges képet lát a másik thread munkájáról — data race és visibility bug következhet be.
Hova illeszkedik?
A JMM a Java nyelv/könyvtárak és az alapul szolgáló hardver közé helyezkedik el. Ez az a szerződés, amelyre:
- A könyvtárírók (pl.
java.util.concurrent) támaszkodnak biztonságos absztrakciók felépítésekor. - Az alkalmazásfejlesztők implicit módon hivatkoznak, valahányszor
volatile-t,synchronized-ot vagyjava.util.concurrentprimitíveket használnak. - A JVM implementáció szerzőjének be kell tartania, amikor natív kódra fordít bármely hardverplatformon.
2. Alapfogalmak
2.1 A probléma JMM nélkül
Képzeljünk el két thread-et, amelyek az x és flag változókat osztják meg:
Példa idővonal szinkronizáció nélkül:
- Thread 1 beírja, hogy
x = 1. - Thread 1 beírja, hogy
flag = true. - Thread 2 azt látja, hogy
flag == true. - Thread 2 ennek ellenére még mindig láthatja, hogy
x == 0, mert a láthatóság nem garantált.
Szinkronizáció nélkül:
- A fordító az Thread 1-ben átrendezheti a két írást (flag előbb, mint x).
- Thread 2 olvashat a saját CPU cache-éből, és láthatja
flag = true-t, miközben mégx = 0. - A CPU store buffer-e eltérő sorrendben ürítheti ki az írásokat.
Mindez megtörténhet valódi hardveren (különösen ARM és POWER architektúrán).
2.2 Happens-Before (HB) reláció
A happens-before reláció a JMM magja. Ha A happens-before B, akkor:
- Az A összes mellékhatása (és minden, ami A előtt HB) látható B számára.
- A JVM/CPU nem rendezheti át ezeket oly módon, hogy sértse ezt a garanciát.
A HB nem valós idejű sorrend. A lehet happens-before B, még akkor is, ha B fizikailag nanoszekundumokkal korábban futott — ez logikai sorrendezési garancia.
Beépített HB szabályok
| Szabály | Leírás |
|---|---|
| Program order | Egy thread-en belül minden akció happens-before az utána következő akció. |
| Monitor unlock | Egy monitor feloldása (unlock) happens-before ugyanazon monitor bármely következő lezárása (lock). |
| Volatile write | Egy volatile mezőre való írás happens-before ugyanazon mező bármely következő olvasása. |
| Thread start | A thread.start() hívás happens-before az indított thread-ben végrehajtott bármely akció. |
| Thread termination | Egy thread összes akciója happens-before bármely thread, amely join()-nal vagy isAlive()-val észleli a leállást. |
| Interruption | Az interrupt() hívás happens-before az interrupted thread észleli az megszakítást. |
| Finalizer | Egy konstruktor befejezése happens-before az adott objektum finalizer-ének kezdete. |
| Tranzitivitás | Ha A HB B és B HB C, akkor A HB C. |
HB vizualizálva
HB a monitor szabály alapján:
- Thread 1 beírja, hogy
x = 1. - Thread 1 feloldja a monitort.
- Thread 2 lezárja ugyanazt a monitort.
- Thread 2 olvasáskor már köteles látni az
x = 1értéket.
2.3 Memóriaarchitektúra és a visibility probléma
Szinkronizáció nélkül a láthatóság tipikusan két szinten dől el:
- Thread-local cache vagy regiszterek — a thread itt tarthat meg elavult értékeket.
- Main memory — a frissített érték már itt lehet, de a többi thread nem köteles azonnal látni.
Ezért fordulhat elő, hogy Thread 2 egy régi értéket olvas tovább, miközben Thread 1 már kiírta az újat.
Ha nincs happens-before él a Thread 1 írása és a Thread 2 olvasása között, a JMM semmilyen garanciát nem ad arra, hogy Thread 2 valaha látni fogja a frissített értéket. Ez a visibility probléma.
2.4 Atomicity
Az atomicity azt jelenti, hogy egy műveletet egyetlen, oszthatatlan egységként hajtanak végre — egyetlen más thread sem figyelhet meg részlegesen elvégzett állapotot.
| Művelet | Atomikus? | Megjegyzés |
|---|---|---|
int / boolean / byte / short / char / float olvasás/írás |
✅ | A JLS garantálja |
long / double olvasás/írás |
⚠️ | 32-bites JVM-en NEM atomikus (két 32-bites művelet) |
volatile long / volatile double olvasás/írás |
✅ | volatile kényszeríti az atomikus 64-bites hozzáférést |
i++ (bármely típus) |
❌ | Read-modify-write: három külön művelet |
AtomicInteger.incrementAndGet() |
✅ | CAS (compare-and-swap) hardveres utasítást használ |
Kulcsgondolat: Még ha egy mező
volatileis, az összetetti++művelet nem atomikus. Beolvas, növel, majd visszaír — egy másik thread közbeszúródhat az olvasás és az írás közé.
2.5 `volatile`
Egy mező volatile-nak deklarálása két garanciát nyújt:
- Visibility: A
volatilemezőre való írás happens-before az adott mező bármely következő olvasása. - Átrendezés megakadályozása: A JVM memory barrier-eket szúr be a volatile hozzáférések köré, megakadályozva a fordítót és a CPU-t, hogy közönséges olvasásokat/írásokat rendezzen át egy volatile hozzáférésen keresztül.
Mit NEM garantál a volatile:
- Összetett műveletek atomicitását (
flag++nem atomikus) - Kizárást (mutual exclusion)
Mikor érdemes volatile-t használni:
- Egyszerű állapotjelzők, amelyeket több thread olvas (pl.
volatile boolean running) - Immutable objektumreferencia biztonságos közzétételéhez (egy író, sok olvasó)
- Double-checked locking (DCL) minta — a referencia mezőnek kötelezően volatile-nak kell lennie
2.6 `synchronized`
A synchronized egyszerre biztosít:
- Kizárást (atomicity a blokkon belül): Egyszerre csak egy thread tartja a monitor-t.
- Visibility (happens-before): Az unlock előtti összes írás látható minden olyan thread számára, amely ezt követően ugyanazt a lock-ot szerzi meg.
Thread 1 Thread 2
synchronized(lock) { synchronized(lock) {
x = 1; ──── HB ────► read x → 1 ✅
y = 2; read y → 2 ✅
} }
2.7 Memory Barrier-ek / Fence-ek
A volatile és a synchronized memory barrier utasításokra fordítódik le, amelyek megakadályozzák a CPU-t, hogy load-okat és store-okat rendeljen át a barrier-en keresztül.
| Barrier típus | Hatás |
|---|---|
| LoadLoad | Egyetlen load sem rendezhető egy korábbi load elé |
| StoreStore | Egyetlen store sem rendezhető egy korábbi store elé |
| LoadStore | Egyetlen store sem rendezhető egy korábbi load elé |
| StoreLoad | Egyetlen load sem rendezhető egy korábbi store elé — a legköltségesebb |
Egy volatile írás StoreStore barrier-t szúr be elé, StoreLoad barrier-t utána.
Egy volatile olvasás LoadLoad + LoadStore barrier-t szúr be utána.
A synchronized hatékonyan teljes fence-t szúr be a lock megszerzésekor és elengedésekor.
3. Gyakorlati használat
Mikor érdemes `volatile`-t használni
- Egyszerű boolean flag-ek vagy státuszjelzők (
volatile boolean shutdown) - Egyetlen immutable objektumreferencia biztonságos közzétételéhez
- A double-checked locking (DCL) referencia mezőjéhez
- Számlálók, ahol csak visibility szükséges (egy thread ír, mások csak olvasnak)
Mikor érdemes `synchronized`-ot használni
- Bármely kizárást (mutual exclusion) igénylő esetben (check-then-act, read-modify-write)
- Amikor több mezőt kell atomikusan együtt frissíteni
- Ha a
volatileegyedül nem elegendő (összetett műveletek)
Mikor érdemes `AtomicXxx`-et használni
- Erősen versengett single-variable számláló vagy akkumulátor
- CAS-alapú non-blocking algoritmusok
- Részesítsd előnyben az
AtomicInteger-t,AtomicLong-ot,AtomicReference-t avolatile+ manuális CAS helyett
Mikor érdemes Immutable objektumokat használni
Ha egy objektum immutable (minden mező final, a konstruktorban kerül beállításra, this nem szökik el), bármilyen mechanizmuson keresztül biztonságosan közzétehető — beleértve a sima értékadást is. A JMM speciálisan kezeli a final mezőket: értékeik garantáltan láthatók a konstruktor befejezése után.
Safe Publication
Egy objektum biztonságosan közzétett (safely published), ha a rá mutató referenciát megfelelően szinkronizált mechanizmuson keresztül teszik láthatóvá más thread-ek számára:
| Mechanizmus | Miért biztonságos |
|---|---|
static inicializáló |
Az osztálybetöltést a JVM szinkronizálja |
final mező |
A JMM garantálja a befagyasztást a konstruktor után |
volatile mező |
Volatile írás HB volatile olvasás |
| Megfelelően zárolt mező | Monitor szabály |
java.util.concurrent gyűjtemények |
Belső volatile/lock használat |
Double-Checked Locking (DCL)
A DCL egy elterjedt singleton minta. volatile nélkül Java 5 előtt hibás volt:
// ❌ HIBÁS — a referencia részlegesen inicializált állapotban látható
class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // 1. ellenőrzés (lock nélkül)
synchronized (Singleton.class) {
if (instance == null) { // 2. ellenőrzés (lock-kal)
instance = new Singleton(); // átrendezés lehetséges!
}
}
}
return instance;
}
}
new Singleton() három művelet: allokálás, mezők inicializálása, referencia értékadása. A JIT átrendezheti az értékadást az inicializálás elé. Egy másik thread null-tól eltérő, de részlegesen inicializált objektumot láthat.
// ✅ HELYES — volatile megakadályozza az értékadás átrendezését
class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4. Kód példák
1. példa — Visibility bug (végtelen ciklus)
// volatile nélkül ez a ciklus sosem ér véget!
public class VisibilityBug {
private static boolean running = true; // ❌ nem volatile
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
while (running) { /* pörgés */ }
System.out.println("Megállt.");
});
worker.start();
Thread.sleep(100);
running = false; // Thread 1 ír, de a worker sosem láthatja!
System.out.println("running = false beállítva");
}
}
Megoldás: private static volatile boolean running = true;
2. példa — Hibás vs helyes Double-Checked Locking
// ❌ Hibás DCL — hiányzó volatile
class BrokenSingleton {
private static BrokenSingleton instance;
public static BrokenSingleton get() {
if (instance == null) {
synchronized (BrokenSingleton.class) {
if (instance == null) instance = new BrokenSingleton();
}
}
return instance; // Részlegesen inicializált objektumot adhat vissza!
}
}
// ✅ Helyes DCL — volatile az instance-on
class CorrectSingleton {
private static volatile CorrectSingleton instance;
public static CorrectSingleton get() {
if (instance == null) {
synchronized (CorrectSingleton.class) {
if (instance == null) instance = new CorrectSingleton();
}
}
return instance;
}
}
3. példa — volatile számláló csapda (nem atomikus!)
public class VolatileCounter {
private volatile int count = 0; // ❌ volatile NEM teszi atomikussá a ++-t!
public void increment() {
count++; // olvasás → növelés → írás (3 lépés, versenyhelyzet lehetséges)
}
public int get() { return count; }
}
// 1000 thread-del, mindegyik egyszer hívva az increment()-et, a végeredmény < 1000 lehet!
4. példa — Helyes atomikus számláló
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // ✅ CAS-alapú, lock-free, atomikus
}
public int get() { return count.get(); }
}
5. példa — Biztonságos közzététel final mezőkön keresztül
// Immutable objektum: bármilyen referencián keresztül biztonságosan közzétehető
public final class ImmutablePoint {
private final int x;
private final int y;
public ImmutablePoint(int x, int y) {
this.x = x;
this.y = y;
// A konstruktor visszatérése után minden olvasó garantáltan látja x-et és y-t
}
public int getX() { return x; }
public int getY() { return y; }
}
// Még egy sima (nem volatile) értékadás is biztonságos immutable objektumokhoz
// statikus inicializálón keresztül közzétéve:
public class Config {
public static final ImmutablePoint ORIGIN = new ImmutablePoint(0, 0); // ✅ biztonságos
}
Gyakori csapda — Flag ellenőrzése szinkronizáció nélkül
// ❌ race condition: check-then-act atomicitás nélkül
if (!map.containsKey(key)) {
map.put(key, computeValue()); // közbeszúródhat egy másik thread az ellenőrzés és a put közé
}
// ✅ ConcurrentHashMap.computeIfAbsent használata az atomikus check-then-put-hoz
map.computeIfAbsent(key, k -> computeValue());
5. Trade-offok
| Szempont | volatile |
synchronized |
AtomicXxx |
|---|---|---|---|
| ⚡ Teljesítmény | Alacsony overhead, ~memory barrier | Magasabb — lock megszerzés/elengedés OS-t vagy spinlock-ot érint | Alacsony–közepes — CAS versengés esetén újra kísérelhet |
| 🔒 Kizárás | ❌ Nincs | ✅ Igen | ✅ Változónként (CAS) |
| 👁️ Visibility | ✅ Igen | ✅ Igen | ✅ Igen |
| 🔢 Összetett műveletek | ❌ Nem atomikus | ✅ Ha azonos blokkon belül | ✅ Metódusonként (pl. compareAndSet) |
| 💾 Memória | Minimális | Monitor objektum overhead | Objektum változónként |
| 🔧 Karbantarthatóság | Egyszerű flag-ekhez | Tiszta szándék, ismerős | Jó számlálókhoz/referenciákhoz |
| 🔄 Skálázhatóság | Magas — nincs blokkolás | Versengés csökkenti az átvitelt | Magas — non-blocking algoritmusok |
A false sharing egy rejtett teljesítményproblémát jelent: ha két volatile mező ugyanabban a CPU cache line-ban van, minden írás érvényteleníti az egész cache line-t az összes CPU számára, még akkor is, ha különböző thread-ek különböző változókhoz férnek hozzá. A hot mezőket elválasztva padding vagy @jdk.internal.vm.annotation.Contended segítségével lehet megoldani.
6. Gyakori hibák
❌ 1. hiba: Azt feltételezni, hogy `volatile` atomikussá teszi az összetett műveleteket
// ❌ volatile NEM teszi atomikussá a ++-t!
private volatile int counter = 0;
public void increment() { counter++; } // DATA RACE
// ✅ Használj AtomicInteger-t
private final AtomicInteger counter = new AtomicInteger(0);
public void increment() { counter.incrementAndGet(); }
❌ 2. hiba: Double-checked locking `volatile` nélkül
// ❌ Java 5 előtt hibás, és még ma is helytelen — a JIT átrendezheti
private static Resource instance;
// ...
if (instance == null) { synchronized(...) { if (instance == null) instance = new Resource(); } }
// ✅ Kötelezően volatile-nak kell lennie
private static volatile Resource instance;
❌ 3. hiba: Mutable állapot megosztása bármilyen szinkronizáció nélkül
// ❌ Mindkét thread szinkronizáció nélkül fér hozzá a 'list'-hez — ConcurrentModificationException / adatvesztés
List<String> list = new ArrayList<>();
// Thread 1: list.add("a");
// Thread 2: list.add("b");
// ✅ Használj thread-safe gyűjteményt
List<String> list = Collections.synchronizedList(new ArrayList<>());
// vagy
List<String> list = new CopyOnWriteArrayList<>();
❌ 4. hiba: Túlzott szinkronizáció
// ❌ Immutable értéken minden olvasást zárni — szükségtelen versengés
public synchronized String getImmutableConfig() { return config; }
// ✅ Immutable / final mezőkhoz nem kell zárás
private final String config = "value";
public String getConfig() { return config; }
❌ 5. hiba: A happens-before összekeverése a valós idejű sorrenddel
// TÉVES gondolkodás: "Thread 1 hamarabb ír, mint Thread 2 olvas, tehát Thread 2 látni fogja"
// A HB LOGIKAI garancia, nem időbeli.
// HB él (volatile/synchronized/stb.) nélkül NINCS visibility garancia,
// függetlenül attól, hogy Thread 1 mikor futott valós időben.
7. Senior-szintű meglátások
JSR-133 és a Java 5 újraírás
Az eredeti Java Memory Model (JDK 1.0–1.4) közismerten hibás volt — még a double-checked locking helyes viselkedésére sem adott garanciát. A JSR-133 újraírta a JMM-et Java 5-ben, bevezetve a happens-before formalizmust, a megerősített volatile szemantikát és a final mező garanciákat, amelyek a modern Java konkurenciájának alapját képezik.
CPU memóriamodellek: x86 TSO vs ARM
A JMM hardverfüggetlen, de implementációs költsége CPU-nként eltér:
- x86 (TSO — Total Store Order): Az x86-nak már eleve viszonylag erős memóriamodellje van. A
volatileolvasások lényegében ingyenesek (csak egy load); csak avolatileírásokhoz kellLOCK XCHGvagyMFENCE. Ezért sok JMM bug csak ARM-on vagy POWER-en mutatkozik meg. - ARM/POWER (gyenge memóriamodell): Mindkét irányhoz (olvasás és írás) explicit
dmb/syncbarrier utasítások szükségesek, ami drágábbá teszi avolatile-t.
False Sharing és `@Contended`
Ha két hot volatile mező ugyanabban a CPU cache line-ban van (jellemzően 64 byte), bármelyik írása érvényteleníti az egész cache line-t az összes CPU számára — ez a false sharing, egy csendes teljesítmény-gyilkos a nagy áteresztőképességű konkurens kódban.
// ❌ False sharing: counter és flag valószínűleg ugyanabban a cache line-ban van
class Shared {
volatile long counter = 0;
volatile boolean flag = false;
}
// ✅ @Contended (JDK belső, --add-opens vagy JVM flag szükséges)
// vagy manuális padding
class Padded {
volatile long counter = 0;
long p1, p2, p3, p4, p5, p6, p7; // 56 byte padding
volatile boolean flag = false;
}
VarHandle (Java 9+)
A java.lang.invoke.VarHandle részletes memóriarendezési szemantika feletti kontrollt biztosít a teljes volatile overhead nélkül:
| Hozzáférési mód | Rendezési garancia |
|---|---|
getPlain / setPlain |
Nincs rendezés (nem volatile-ként) |
getOpaque / setOpaque |
Koherens, változónkénti rendezés |
getAcquire / setRelease |
Acquire/release szemantika (olcsóbb, mint a teljes volatile) |
getVolatile / setVolatile |
Teljes volatile szemantika |
compareAndSet |
CAS teljes volatile szemantikával |
Az acquire/release (amelyet a java.util.concurrent is széles körben alkalmaz) olcsóbb, mint a teljes volatile, gyenge memóriamodellű architektúrákon, mivel csak egyirányú barrier-eket igényel.
`final` mezők és Safe Publication
A JMM speciális garanciát ad a final mezőkre: ha a konstruktor lefut és a referencia nem szökik el a konstruktorból, minden thread helyesen inicializált értékeket fog látni az összes final mezőn semmilyen további szinkronizáció nélkül. Ez az immutabilitás-alapú safe publication alapja.
Lock-free algoritmusok és CAS
Az AtomicInteger, AtomicReference stb. a compare-and-swap (CAS) műveletet használja — egyetlen atomikus CPU utasítást (CMPXCHG x86-on), amely egy oszthatatlan lépésben olvas, összehasonlít és feltételesen ír. Ez lehetővé tesz non-blocking algoritmusokat, amelyek mérsékelt versengés esetén nagyobb áteresztőképességgel rendelkeznek, mint lock-alapú megfelelőik. Igen erős versengés esetén a CAS retry loop-ok (ABA probléma, versengő CAS) rosszabb teljesítményre degradálódhatnak, mint egy jól hangolt lock.
8. Szószedet
| Fogalom | Definíció |
|---|---|
| JMM | Java Memory Model — a formális specifikáció (JSR-133), amely meghatározza, hogyan osztják meg a thread-ek a memóriát. |
| Happens-Before | Logikai sorrendezési garancia: ha A HB B, akkor A összes hatása látható B számára. |
| Visibility | Annak lehetősége, hogy az egyik thread írása egy másik thread olvasásában megfigyelhető-e. |
| Atomicity | Egy művelet tulajdonsága: egyetlen oszthatatlan egységként hajtódik végre, közbülső állapot nem figyelhető meg. |
| volatile | Java kulcsszó, amely kikényszeríti a visibility-t és megakadályozza az átrendezést, de nem biztosít kizárást. |
| synchronized | Java kulcsszó, amely monitor lock-on keresztül biztosít kizárást és visibility-t. |
| Memory Barrier | CPU/fordító utasítás, amely megakadályozza a read/write műveletek átrendezését a barrier-en keresztül. |
| Race Condition | Hiba, amelynél az eredmény a thread-ek relatív ütemezésétől függ. |
| Data Race | Két thread egyidejűleg fér hozzá ugyanahhoz a memóriahelyre, legalább az egyikük ír, szinkronizáció nélkül. |
| Monitor | A synchronized által Java-ban használt, objektumonkénti lock mechanizmus. |
| Safe Publication | Egy objektumreferencia láthatóvá tétele más thread-ek számára oly módon, hogy az objektum állapota is garantáltan látható. |
| Reordering | A fordító vagy CPU megváltoztatja a memóriaműveletek sorrendjét (egyszálasnál biztonságos, többszálasnál veszélyes). |
| Store Buffer | CPU hardveres puffer, amely a függőben lévő írásokat tartja, mielőtt elérnek a cache-be/főmemóriába. |
| Cache Coherence | Hardveres protokoll (pl. MESI), amely biztosítja, hogy minden CPU végül megegyezzen egy megosztott memóriahely értékéről. |
| False Sharing | Két thread akaratlanul verseng ugyanazon CPU cache line-on, egymástól független mezők közelsége miatt. |
| CAS | Compare-And-Swap — atomikus CPU utasítás, amelyet lock-free adatstruktúrák implementálásához használnak. |
| Acquire/Release | Gyengébb memóriarendezési szemantika: acquire (olvasás után) megakadályozza az azt követő read/write-ok előre mozgását; release (írás előtt) megakadályozza a megelőző read/write-ok hátra mozgását. |
9. Cheatsheet
- 🔑 A HB a JMM alapszabálya: hozz létre happens-before élt, különben nincs visibility garancia.
- 🏷️
volatile= visibility + átrendezés megakadályozása; NEM kizárás vagy összetett művelet atomicitása. - 🔒
synchronized= kizárás + visibility; akkor használd, ha több mezőt vagy összetett műveleteket kell atomikusan kezelni. - ⚛️
AtomicInteger/AtomicReference= lock-free, CAS-alapú atomikus műveletek egyetlen változóhoz. - ♾️ Az
i++SOHA nem atomikus, mégvolatilemezőn sem — olvasás + módosítás + írás. - 🏗️ A DCL mintához
volatileszükséges a referencia mezőn a részlegesen inicializált objektumok elkerüléséhez. - 🧊 A
finalmezők konstruktor után szabadon biztonságosak — az immutable objektumok a legegyszerűbb szálbiztonságot nyújtják. - 🐌 A false sharing csendben öli a teljesítményt; védd a hot volatile mezőket padding-gel vagy
@Contended-del. - 🔧 A
VarHandle(Java 9+) acquire/release szemantikát kínál — olcsóbb, mint a teljes volatile gyenge memóriamodellű CPU-kon. - ⚠️ Happens-before ≠ valós idő: logikai rendezés, nem kronológiai.
🎮 Játékok
8 kérdés