Synchronized: co to jest, zasada działania i użycie w Java

Autor: IT Sectr Opublikowano: 2026-03-19 Czas czytania: 8 min

Synchronized to wbudowany mechanizm synchronizacji w języku Java, zapewniający ekskluzywny dostęp do sekcji krytycznych kodu. Według Oracle, 2024, modyfikator synchronized gwarantuje, że tylko jeden wątek może wykonywać oznaczoną metodę lub blok w określonym momencie. Mechanizm ten opiera się na monitorach — fundamentalnej koncepcji systemów operacyjnych, zapewniającej poprawne działanie aplikacji wielowątkowych na wszystkich poziomach złożoności.

Najważniejsze

  • Synchronized — słowo kluczowe Java dla bezpiecznego dostępu do danych.
  • Monitor obiektu — wewnętrzny mechanizm, na którym opiera się synchronizacja.
  • Metoda synchronized blokuje całą metodę na poziomie instancji lub klasy.
  • Blok synchronized pozwala synchronizować tylko część kodu.
  • Deadlock — jeden z głównych problemów przy zagnieżdżonej synchronizacji.

Co to jest synchronized?

Synchronized to słowo kluczowe w Java, które gwarantuje, że tylko jeden wątek w danej chwili wykonuje chroniony fragment kodu, zapobiegając uszkodzeniu danych przy równoległym dostępie. Pojawiło się w pierwszej wersji Java i pozostaje najprostszym sposobem zapewnienia bezpieczeństwa wątkowego dla programistów na każdym poziomie zaawansowania.

Definicja i rola w Java

Modyfikator synchronized rozwiązuje dwa zadania: wzajemne wykluczanie (mutual exclusion) i widoczność zmian (visibility). Gdy wątek opuszcza blok synchronized, wszystkie zmiany są gwarantowanie widoczne dla innych wątków wchodzących do bloku zsynchronizowanego na tym samym obiekcie.

Synchronized można zastosować do całej metody lub do dowolnego bloku kodu z określeniem obiektu-monitora. W obu przypadkach JVM wstawia instrukcje monitorenter i monitorexit na poziomie kodu bajtowego.

Przesłanki powstania

W aplikacjach wielowątkowych bez synchronizacji występuje stan wyścigu (race condition) — gdy dwa wątki jednocześnie zmieniają te same dane, prowadząc do nieprzewidywalnych rezultatów. Synchronized stał się pierwszym i głównym narzędziem Java do walki z tym problemem, oferując prostą deklaratywną składnię dostępną dla każdego programisty.

Jak działa synchronized?

Mechanizm synchronized opiera się na koncepcji monitora — wysokopoziomowego prymitywu synchronizacji wbudowanego w każdy obiekt Java. Monitor jest kojarzony z obiektem przy pierwszym użyciu bloku synchronized na nim.

Monitor obiektu

Każdy obiekt w Java ma powiązany monitor. Gdy wątek wchodzi do bloku synchronized, przechwytuje monitor obiektu. Jeśli monitor jest już zajęty przez inny wątek, wątek jest blokowany do momentu zwolnienia. W kodzie bajtowym odpowiada temu para instrukcji monitorenter i monitorexit.

Stany blokady (biased locking)

JVM optymalizuje synchronized przez kilka poziomów: biased locking (przesunięta blokada) dla dostępu jednowątkowego, lightweight locking (lekka blokada) przy słabej konkurencji i heavyweight locking (ciężka blokada) przy intensywnej konkurencji z udziałem OS. Te poziomy zwiększają wydajność bez zmiany kodu.

java
class Counter {
    private int count = 0;

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

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

Reguła happens-before

Synchronized ustanawia relację happens-before: wszystkie działania w wątku przed wyjściem z bloku synchronized są widoczne dla innego wątku po wejściu do bloku zsynchronizowanego na tym samym obiekcie. Gwarantuje to nie tylko wzajemne wykluczanie, ale także spójność danych dla wszystkich wątków.

Metoda synchronized vs blok

Java oferuje dwa sposoby zastosowania synchronized: na poziomie metody i na poziomie bloku. Wybór między nimi wpływa na wydajność i ziarnistość synchronizacji.

Metoda synchronized

Oznaczając metodę modyfikatorem synchronized, automatycznie synchronizujesz ją na bieżącej instancji (dla zwykłej metody) lub na obiekcie Class (dla metody statycznej). To najprostszy sposób zapewnienia wzajemnego wykluczania, ale często jest zbędny, jeśli sekcja krytyczna stanowi tylko małą część metody, a reszta kodu nie wymaga synchronizacji.

Blok synchronized

Blok synchronized daje precyzyjną kontrolę: określasz obiekt-monitor i synchronizujesz tylko niezbędny fragment kodu, pozostawiając resztę metody poza blokadą. Minimalizuje to czas utrzymywania monitora i zwiększa ogólną wydajność aplikacji w środowisku wielowątkowym, ponieważ inne wątki mogą równolegle wykonywać niepowiązany kod, nie czekając na zwolnienie monitora.

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

    public void process() {
        // kod poza sekcją krytyczną - bez synchronizacji
        prepareData()

        synchronized (lock) {
            // tylko ten blok jest chroniony
            updateSharedState()
        }

        // kontynuacja bez blokady
        cleanup()
    }
}
KryteriumMetoda synchronizedBlok synchronized
Monitorthis (instancja) lub Classdowolny obiekt
Ziarnistośćcała metodatylko potrzebny kod
Czytelnośćwysokaśrednia
Wydajnośćniższa przy dużej metodziewyższa przy małej sekcji krytycznej

Synchronized w Android

W programowaniu na Androida synchronized jest szeroko stosowany do ochrony SharedPreferences, dostępu do bazy danych i komponentów UI. Jednak jego użycie na głównym wątku jest kategorycznie odradzane ze względu na ryzyko zawieszenia interfejsu.

Użycie z SharedPreferences

SharedPreferences w Android zapewnia podstawowe bezpieczeństwo wątkowe, ale przy edycji przez wiele wątków przez Editor może być wymagana zewnętrzna synchronizacja. Blok synchronized z oddzielnym obiektem blokady gwarantuje spójność zmian.

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

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

Ograniczenia w aplikacjach Android

Główne ograniczenie synchronized na Android — blokowanie wątku. W przeciwieństwie do korutyn z Mutex, synchronized blokuje cały wątek systemowy. Na głównym wątku powoduje to ANR. W nowoczesnym programowaniu na Androida zaleca się zastępowanie synchronized korutynami (suspend Mutex) lub typami atomowymi (AtomicInteger).

Alternatywy dla synchronized

Nowoczesny Java i Kotlin oferują kilka alternatyw dla synchronized, z których każda rozwiązuje te same zadania z mniejszymi ograniczeniami lub lepszą wydajnością.

Lock z java.util.concurrent

Interfejs Lock z implementacjami ReentrantLock i ReadWriteLock zapewnia limity czasu, oczekiwanie z możliwością przerwania i wiele kolejek Condition. Jest bardziej elastyczny niż synchronized, ale wymaga jawnego zwolnienia w finally, co zwiększa ryzyko błędu przy zapomnianym unlock.

Klasy atomowe

Klasy AtomicInteger, AtomicLong, AtomicReference i inne wykorzystują algorytmy Lock-Free oparte na CAS (Compare-And-Swap). Są znacznie szybsze od synchronized w scenariuszach z umiarkowaną konkurencją, ponieważ nie blokują wątków, lecz wykonują optymistyczne próby ponowne i nie wymagają przełączania kontekstu przez jądro OS.

ThreadLocal i bezpieczeństwo wątków

ThreadLocal zapewnia alternatywne podejście: każda zmienna ThreadLocal jest izolowana w ramach jednego wątku i nie wymaga synchronizacji do odczytu i zapisu. Całkowicie eliminuje to potrzebę stosowania synchronized dla danych, które nie powinny być współdzielone między wątkami. ThreadLocal jest aktywnie używany w frameworkach (Spring, Hibernate) do przechowywania kontekstu transakcji i sesji.

Korutyny i podejście compose

W projektach Kotlin dla Androida alternatywą dla synchronized jest Mutex z kotlinx.coroutines. Nie blokuje on wątku systemu operacyjnego, lecz zawiesza korutynę do momentu zwolnienia blokady — pozwala to efektywnie wykorzystywać wątki puli i uniknąć ANR przy długim oczekiwaniu na zwolnienie zasobu.

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

val mutex = Mutex()
var counter = 0

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

Wydajność synchronized

Wydajność synchronized znacznie się zmieniła w ostatnich wersjach Java. Wcześniej uważano go za „ciężki“ mechanizm, ale nowoczesne JVM wyeliminowały większość narzutów dzięki zaawansowanym optymalizacjom kompilatora JIT. Przyjrzyjmy się szczegółowo, jak wirtualna maszyna przyspiesza zsynchronizowany kod w czasie wykonania.

Biased Locking i Lock Coarsening

Kompilator JIT JVM stosuje kilka optymalizacji: biased locking eliminuje synchronizację, jeśli blokadę zawsze przechwytuje jeden wątek; lock coarsening łączy sąsiednie bloki synchronized w jeden; lock elimination usuwa synchronizację, jeśli obiekt jest dostępny tylko dla jednego wątku. Te optymalizacje sprawiają, że synchronized jest praktycznie darmowy przy niskiej konkurencji.

Pomiar konkurencji i wybór optymalizacji

JVM określa poziom konkurencji dla każdego obiektu: przy braku konkurencji włączany jest biased locking, przy pojawieniu się drugiego wątku blokada przechodzi w tryb lightweight ze spin-waiting, a dopiero przy długim oczekiwaniu — w heavyweight z systemowym muteksem. Ta eskalacja następuje automatycznie i programista nie musi ręcznie wybierać strategii.

Porównanie z Lock i klasami atomowymi

W nowoczesnych benchmarkach (Java 17+) synchronized wykazuje wydajność porównywalną z ReentrantLock przy niskiej i umiarkowanej konkurencji. Przy wysokiej konkurencji Lock może mieć przewagę dzięki bardziej efektywnej kolejce oczekiwania z obsługą timeoutów i przerwań. Dla systemów o wysokim obciążeniu, gdzie konkurencja jest stała, ReentrantLock w trybie fair daje bardziej przewidywalne zachowanie.

Klasy atomowe (AtomicInteger, AtomicReference) pozostają najszybsze dla prostych liczników i flag dzięki implementacji Lock-Free na CAS. W ogóle nie blokują wątków — przy konflikcie operacja jest po prostu powtarzana w pętli. Daje to wzrost wydajności 3-5 razy w porównaniu z synchronized przy operacjach inkrementacji licznika na 4-8 wątkach.

Często zadawane pytania

Jaka jest różnica między synchronized a volatile?

Synchronized zapewnia zarówno wzajemne wykluczanie, jak i widoczność. Volatile gwarantuje tylko widoczność zmian — zapis do zmiennej volatile jest widoczny dla wszystkich wątków, ale nie zapobiega jednoczesnej modyfikacji, czyli nie chroni przed stanem wyścigu.

Czy synchronized może spowodować deadlock?

Tak, deadlock jest możliwy przy zagnieżdżonej synchronizacji z różną kolejnością monitorów. Na przykład jeden wątek wywołuje synchronized(a) { synchronized(b) }, a drugi — synchronized(b) { synchronized(a) }. Unikaj zagnieżdżonych bloków synchronized lub ustal jednolitą kolejność monitorów.

Czym jest monitor w Java?

Monitor to mechanizm synchronizacji powiązany z każdym obiektem Java. Gwarantuje, że tylko jeden wątek wykonuje kod synchronized na danym obiekcie. Monitor obejmuje blokadę, kolejkę oczekiwania i pulę wątków oczekujących na powiadomienie przez wait/notify.

Czy Lock jest szybszy niż synchronized?

W nowoczesnych wersjach Java (17+) synchronized nie ustępuje Lock pod względem wydajności dzięki optymalizacjom JIT (biased locking, lock coarsening). Lock jest preferowany nie ze względu na szybkość, ale z powodu dodatkowych możliwości: limitów czasu, oczekiwania z możliwością przerwania i wielu Condition.

Jak synchronized działa z metodami statycznymi?

Statyczna metoda synchronized używa monitora obiektu Class danej klasy, a nie instancji. Oznacza to, że synchronizacja obejmuje wszystkie instancje klasy. Niestatyczne i statyczne metody synchronized używają różnych monitorów i nie blokują się nawzajem.

Podsumowanie

  • Synchronized — wbudowany mechanizm synchronizacji Java oparty na monitorach.
  • Monitor obiektu — wewnętrzna struktura JVM zapewniająca wzajemne wykluczanie.
  • Metody wait/notify są używane tylko wewnątrz bloków i metod synchronized.
  • Blok synchronized jest preferowany nad metodą ze względu na drobniejszą ziarnistość synchronizacji.
  • Happens-before gwarantuje widoczność zmian między wątkami przy synchronizacji na tym samym obiekcie.
  • Deadlock — główne ryzyko przy zagnieżdżonej synchronizacji z różną kolejnością monitorów.
  • Alternatywy — Lock, klasy atomowe i korutyny z suspending Mutex.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również