Synchronized: mi ez, működési elv és használat Java-ban

Szerző: IT Sectr Megjelenés: 2026-03-19 Olvasási idő: 8 perc

Synchronized egy beépített szinkronizációs mechanizmus a Java nyelvben, amely kizárólagos hozzáférést biztosít a kód kritikus szekcióihoz. A Oracle, 2024 szerint a synchronized módosító garantálja, hogy csak egy szál hajthatja végre a megjelölt metódust vagy blokkot egy adott pillanatban. Ez a mechanizmus monitorokon alapul — az operációs rendszerek alapvető koncepcióján, amely biztosítja a többszálú alkalmazások helyes működését a komplexitás minden szintjén.

Főbb pontok

  • Synchronized — Java kulcsszó a szálbiztos adathozzáféréshez.
  • Objektummonitor — a belső mechanizmus, amelyen a szinkronizáció alapul.
  • Synchronized metódus az egész metódust zárolja példány- vagy osztályszinten.
  • Synchronized blokk lehetővé teszi a kód csak egy részének szinkronizálását.
  • Deadlock — az egyik fő probléma beágyazott szinkronizáció esetén.

Mi az a synchronized?

Synchronized egy kulcsszó Java-ban, amely garantálja, hogy egyszerre csak egy szál hajtja végre a védett kódrészt, megakadályozva az adatok sérülését párhuzamos hozzáférés esetén. A Java első verziójában jelent meg, és a legegyszerűbb módja maradt a szálbiztonság biztosításának minden szintű fejlesztő számára.

Definíció és szerepe Java-ban

A synchronized módosító két feladatot old meg: kölcsönös kizárás (mutual exclusion) és a változtatások láthatósága (visibility). Amikor egy szál elhagy egy synchronized blokkot, az összes változtatás garantáltan látható más szálak számára, amelyek ugyanazon az objektumon szinkronizált blokkba lépnek.

A synchronized alkalmazható a teljes metódusra vagy egy tetszőleges kódblokkra a monitor-objektum megadásával. Mindkét esetben a JVM a monitorenter és monitorexit utasításokat illeszti be bájtkód szinten.

A megjelenés előzményei

A többszálú alkalmazásokban szinkronizáció nélkül versenyhelyzet (race condition) lép fel — amikor két szál egyidejűleg módosítja ugyanazokat az adatokat, kiszámíthatatlan eredményekhez vezetve. A synchronized lett a Java első és fő eszköze a probléma leküzdésére, egyszerű deklaratív szintaxist biztosítva, amely minden fejlesztő számára elérhető.

Hogyan működik a synchronized?

A synchronized mechanizmusa a monitor koncepcióján alapul — egy magas szintű szinkronizációs primitíven, amely minden Java objektumba be van építve. A monitor az objektumhoz kapcsolódik a synchronized blokk első használatakor.

Objektummonitor

Minden Java objektumnak van egy kapcsolódó monitorja. Amikor egy szál belép egy synchronized blokkba, átveszi az objektum monitorját. Ha a monitor már egy másik szál által foglalt, a szál blokkolva van a felszabadulásig. Bájtkód szinten ennek a monitorenter és monitorexit utasításpár felel meg.

Zárolási állapotok (biased locking)

A JVM több szinten optimalizálja a synchronized-t: biased locking (elforgatott zárolás) egyszálú hozzáféréshez, lightweight locking (könnyű zárolás) gyenge versengésnél és heavyweight locking (nehéz zárolás) intenzív versengésnél az OS részvételével. Ezek a szintek növelik a teljesítményt a kód megváltoztatása nélkül.

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

A happens-before szabály

A synchronized happens-before kapcsolatot hoz létre: minden művelet egy szálban a synchronized blokk elhagyása előtt látható egy másik szál számára az ugyanazon az objektumon szinkronizált blokkba való belépés után. Ez nemcsak a kölcsönös kizárást, hanem az adatok konzisztenciáját is garantálja az összes szál számára.

Synchronized metódus vs blokk

A Java két módot kínál a synchronized alkalmazására: metódus szinten és blokk szinten. A választás közöttük befolyásolja a teljesítményt és a szinkronizáció granularitását.

Synchronized metódus

Egy metódus synchronized módosítóval való megjelölésével automatikusan szinkronizálja azt az aktuális példányon (hétköznapi metódus esetén) vagy a Class objektumon (statikus metódus esetén). Ez a legegyszerűbb módja a kölcsönös kizárás biztosításának, de gyakran túlzó, ha a kritikus szekció csak a metódus egy kis részét képezi, és a kód többi része nem igényel szinkronizációt.

Synchronized blokk

A synchronized blokk pontos vezérlést biztosít: megadja a monitor-objektumot, és csak a szükséges kódrészt szinkronizálja, a metódus többi részét a zároláson kívül hagyva. Ez minimalizálja a monitor birtoklásának idejét és növeli az alkalmazás általános teljesítményét többszálú környezetben, mivel más szálak párhuzamosan végrehajthatnak nem kapcsolódó kódot anélkül, hogy a monitor felszabadulására várnának.

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // kód a kritikus szakaszon kívül - szinkronizálás nélkül
        prepareData()

        synchronized (lock) {
            // csak ez a blokk védett
            updateSharedState()
        }

        // folytatás zárolás nélkül
        cleanup()
    }
}
KritériumSynchronized metódusSynchronized blokk
Monitorthis (példány) vagy Classbármely objektum
Granularitásteljes metóduscsak szükséges kód
Olvashatóságmagasközepes
Teljesítményalacsonyabb nagy metódusnálmagasabb kis kritikus szekciónál

Synchronized Android-ban

Android fejlesztésben a synchronized széles körben használatos a SharedPreferences, adatbázis-hozzáférés és UI komponensek védelmére. Használata a főszálon azonban kifejezetten nem ajánlott a felület lefagyásának kockázata miatt.

Használat SharedPreferences-szel

A SharedPreferences Android-ban alapvető szálbiztonságot nyújt, de több szál általi szerkesztéskor Editor-on keresztül külső szinkronizációra lehet szükség. A synchronized blokk külön zárolási objektummal garantálja a változtatások konzisztenciáját.

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

Korlátozások Android-alkalmazásokban

A synchronized fő korlátozása Android-on — szálblokkolás. A Mutex-szel ellátott korutinokkal ellentétben a synchronized blokkolja a teljes rendszerszálat. A főszálon ez ANR-t okoz. Modern Android fejlesztésben ajánlott a synchronized helyettesítése korutinokkal (suspend Mutex) vagy atomi típusokkal (AtomicInteger).

A synchronized alternatívái

A modern Java és Kotlin több alternatívát kínál a synchronized helyett, amelyek mindegyike ugyanazokat a feladatokat oldja meg kevesebb korlátozással vagy jobb teljesítménnyel.

Lock a java.util.concurrent-ből

A Lock interfész a ReentrantLock és ReadWriteLock implementációkkal időtúllépéseket, megszakítható várakozást és több Condition várakozási sort biztosít. Rugalmasabb, mint a synchronized, de explicit felszabadítást igényel a finally blokkban, ami növeli a hiba kockázatát elfelejtett unlock esetén.

Atomi osztályok

Az AtomicInteger, AtomicLong, AtomicReference és más osztályok CAS (Compare-And-Swap) alapú Lock-Free algoritmusokat használnak. Jelentősen gyorsabbak, mint a synchronized mérsékelt versengésű forgatókönyvekben, mivel nem blokkolják a szálakat, hanem optimista újrapróbálkozásokat végeznek, és nem igényelnek kontextusváltást az OS kernel által.

ThreadLocal és szálbiztonság

A ThreadLocal alternatív megközelítést kínál: minden ThreadLocal változó egy szálon belül van elkülönítve, és nem igényel szinkronizációt olvasáshoz és íráshoz. Ez teljesen kiküszöböli a synchronized szükségességét azon adatok esetében, amelyeket nem kell megosztani a szálak között. A ThreadLocal aktívan használatos keretrendszerekben (Spring, Hibernate) tranzakciós kontextus és munkamenetek tárolására.

Korutinok és compose megközelítés

Androidhoz készült Kotlin projektekben a synchronized alternatívája a Mutex a kotlinx.coroutines-ból. Ez nem blokkolja az operációs rendszer szálát, hanem felfüggeszti a korutint a zárolás felszabadulásáig — ez lehetővé teszi a pool szálak hatékony használatát és az ANR elkerülését az erőforrás felszabadulására való hosszú várakozáskor.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

A synchronized teljesítménye

A synchronized teljesítménye jelentősen megváltozott a Java legújabb verzióiban. Korábban „nehéz„ mechanizmusnak tartották, de a modern JVM-ek a JIT fordító fejlett optimalizálásainak köszönhetően megszüntették a többletterhelés nagy részét. Vizsgáljuk meg részletesen, hogyan gyorsítja fel a virtuális gép a szinkronizált kódot futásidőben.

Biased Locking és Lock Coarsening

A JVM JIT fordítója több optimalizálást alkalmaz: biased locking megszünteti a szinkronizációt, ha a zárolást mindig egy szál veszi át; lock coarsening egyesíti a szomszédos synchronized blokkokat egybe; lock elimination eltávolítja a szinkronizációt, ha az objektum csak egy szál számára elérhető. Ezek az optimalizálások a synchronized-t gyakorlatilag ingyenessé teszik alacsony versengés esetén.

Versengés mérése és optimalizálás kiválasztása

A JVM meghatározza a versengés szintjét minden objektumhoz: versengés hiányában a biased locking aktiválódik, a második szál megjelenésekor a zárolás lightweight módba vált spin-waitinggel, és csak hosszú várakozáskor — heavyweight-be rendszermutex-szel. Ez az eszkaláció automatikusan történik, és a fejlesztőnek nem kell manuálisan stratégiát választania.

Összehasonlítás a Lock és atomi osztályokkal

A modern benchmarkokban (Java 17+) a synchronized a ReentrantLock-hoz hasonló teljesítményt mutat alacsony és mérsékelt versengés esetén. Magas versengésnél a Lock előnyösebb lehet a hatékonyabb várakozási sor miatt, időtúllépések és megszakítások támogatásával. Magas terhelésű rendszerekhez, ahol a versengés állandó, a ReentrantLock fair módban kiszámíthatóbb viselkedést biztosít.

Az atomi osztályok (AtomicInteger, AtomicReference) maradnak a leggyorsabbak egyszerű számlálók és jelzők számára a CAS-on alapuló Lock-Free implementációnak köszönhetően. Egyáltalán nem blokkolják a szálakat — konfliktus esetén a művelet egyszerűen megismétlődik egy ciklusban. Ez 3-5-szörös teljesítménynövekedést ad a synchronized-hez képest számláló növelési műveleteknél 4-8 szálon.

Gyakran ismételt kérdések

Mi a különbség a synchronized és a volatile között?

Synchronized mind a kölcsönös kizárást, mind a láthatóságot biztosítja. A Volatile csak a változtatások láthatóságát garantálja — a volatile változóba írás látható az összes szál számára, de nem akadályozza meg az egyidejű módosítást, vagyis nem véd a versenyhelyzet ellen.

Okozhat-e deadlock-ot a synchronized?

Igen, deadlock lehetséges beágyazott szinkronizációnál eltérő monitor sorrend esetén. Például az egyik szál synchronized(a) { synchronized(b) } hívást végez, a másik pedig — synchronized(b) { synchronized(a) }. Kerülje a beágyazott synchronized blokkokat, vagy rögzítsen egységes monitor sorrendet.

Mi az a monitor Java-ban?

Monitor egy szinkronizációs mechanizmus, amely minden Java objektumhoz kapcsolódik. Garantálja, hogy csak egy szál hajt végre synchronized kódot az adott objektumon. A monitor magában foglalja a zárolást, a várakozási sort és a wait/notify-n keresztül értesítésre váró szálak készletét.

Gyorsabb-e a Lock, mint a synchronized?

A Java modern verzióiban (17+) a synchronized nem marad el a Lock mögött teljesítményben a JIT optimalizálásoknak (biased locking, lock coarsening) köszönhetően. A Lock nem a sebesség miatt előnyösebb, hanem a további képességek miatt: időtúllépések, megszakítható várakozás és több Condition.

Hogyan működik a synchronized statikus metódusokkal?

Statikus synchronized metódus az osztály Class objektumának monitorját használja, nem a példányét. Ez azt jelenti, hogy a szinkronizáció az osztály összes példányára kiterjed. A nem statikus és statikus synchronized metódusok különböző monitorokat használnak, és nem blokkolják egymást.

Összefoglalás

  • Synchronized — beépített Java szinkronizációs mechanizmus monitorok alapján.
  • Objektummonitor — belső JVM struktúra, amely biztosítja a kölcsönös kizárást.
  • A wait/notify metódusok csak synchronized blokkokon vagy metódusokon belül használhatók.
  • Synchronized blokk előnyösebb a metódusnál a finomabb szinkronizációs granularitás miatt.
  • Happens-before garantálja a változtatások láthatóságát a szálak között azonos objektumon történő szinkronizációnál.
  • Deadlock — a fő kockázat beágyazott szinkronizációnál eltérő monitor sorrend esetén.
  • Alternatívák — Lock, atomi osztályok és korutinok suspending Mutex-szel.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is