Architektura vláken a synchronizace v Javě: od procesoru po JVM
Javový kód se kompiluje do bajtového kódu, který JVM převádí na strojové instrukce procesoru. Každé jádro procesoru zpracovává instrukce sekvenčně: za jeden takt zpracuje jednoduchou operaci, například posun bitu v registru. Registry jsou supersnadné paměťové buňky pro ukládání čísel, adres a dat.
Moderní procesory využívají superskálární architekturu. Hyper-Threading umožňuje jednomu hardwarovému vláknu provádět 2–6 instrukcí za takt, čímž vytváří iluzi několika logických vláken na jádru díky rychlému přepínání kontextu.
Počet souběžně spuštěných vláken je omezen počtem jader. Vlákná sdílejí heap paměť, kde se ukládají objekty, zatímco každé vlákno má vlastní stack pro lokální proměnné a reference na objekty.
Race Condition a struktura paměti
Race Condition nastává, když několik vláken současně čte a modifikuje jeden objekt v heapu. Výsledek je nepředvídatelný: závisí na pořadí provedení operací „přečti-změň-zapiš“.
Lokálně kód často funguje správně bez zátěže, ale v produkci pod vícevláknovou zátěží se kolize projevují nepravidelně. Proces v OS alokuje společnou paměť; vlákna uvnitř procesu jsou lehké jednotky provádění, které soutěží o zdroje.
Mechanismy synchronizace: wait/notify a monitory
Každý objekt v Javě má vestavěný monitor (zámek). Blok synchronized zachytí monitor objektu a zajišťuje:
- Mutual Exclusion: pouze jedno vlákno provádí kód uvnitř bloku.
- Visibility: změny jsou viditelné pro jiná vlákna po opuštění bloku (s podporou
volatile).
Monitor obsahuje:
- Entry Set: frontu vláken čekajících na zachycení.
- Wait Set: vlákna ve stavu čekání.
Metody wait(), notify(), notifyAll() fungují pouze uvnitř synchronized bloku stejného objektu, jinak vyhodí IllegalMonitorStateException.
wait(): přesune vlákno do Wait Set.notify(): probudí libovolné vlákno (riziko ztraceného probuzení).notifyAll(): probudí všechna vlákna (preferováno).
Statické synchronized metody blokují monitor třídy (Class), nekolidují s instančními metodami (this).
Pokročilé zámky: ReentrantLock a Semaphore
Pro flexibilní ovládání použijte java.util.concurrent.locks.ReentrantLock:
Lock lock = new ReentrantLock();
lock.lock();
try {
// kritická sekce
} finally {
lock.unlock();
}
Klíčové metody:
tryLock(): zachycení bez čekání (false při neúspěchu).tryLock(timeout, unit): čekání s timeoutem.lockInterruptibly(): přerušitelné zachycení.new ReentrantLock(true): fair locking (podle FIFO, pomalejší).
Semaphore omezuje přístup:
Semaphore sem = new Semaphore(3); // 3 povolení
sem.acquire(); // zachycení
sem.release(); // uvolnění
Problémy vícevláknovosti: Deadlock a Livelock
Deadlock: cyklická závislost zámků. Vlákno A drží lock1, čeká na lock2; vlákno B naopak. Systém zamrzne.
Vyhněte se:
- Blokujte v pevném pořadí.
- Používejte timeouty.
- Minimalizujte vnořené zámky.
Livelock: vlákna jsou aktivní, ale nepostupují. Příklad: dvě vlákna se navzájem ustupují v cyklu, zatěžují CPU bez pokroku.
Synchronní vs asynchronní zpracování ve vláknech
Synchronní metoda blokuje vlákno na I/O (např. dotaz do DB): vlákno nečinně čeká na odpověď.
Asynchronní využívá CompletableFuture – kontejner pro odložený výsledek (pending, completed, failed).
Řetězce operací:
thenApply(): transformace výsledku.thenCompose(): kompozice s jiným Future.thenAccept(): spotřeba bez návratu.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "result")
.thenApply(s -> s.toUpperCase())
.thenAccept(System.out::println);
Tím se vlákno uvolní pro jiné úlohy, což zvyšuje propustnost v vícevláknovém prostředí.
Klíčové body
- Každé jádro provádí jedno hardwarové vlákno; Hyper-Threading emuluje vícenásobnost.
- Race Condition z sdíleného heapu; synchronizace zajišťuje mutual exclusion a visibility.
notifyAll()je lepší nežnotify()kvůli riziku ztraceného probuzení.- ReentrantLock rozšiřuje
synchronized: timeouty, přerušení, fair locking. - CompletableFuture buduje asynchronní potrubí bez blokování vláken.
— Editorial Team
Zatím žádné komentáře.