0Pricing
Java Academy · Lekcja

ReentrantLock a synchronized

Porównuj funkcje ReentrantLock (tryLock, lockInterruptibly, fairness) ze słowem kluczowym synchronized.

ReentrantLock a synchronized to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Słowo kluczowe synchronized

Słowo kluczowe Java synchronized zapewnia wzajemne wykluczanie przy użyciu wbudowanej blokady obiektu (monitora). To proste rozwiązanie, ale z ograniczeniami — bez limitu czasu, sprawiedliwego dostępu i możliwości przerwania.

class Counter {
    private int count = 0;
    
    synchronized void increment() {
        count++; // only one thread at a time
    }
    
    synchronized int get() { return count; }
}

Counter c = new Counter();
// Multiple threads can safely call increment()

Podstawy ReentrantLock

ReentrantLock to jawna blokada z pakietu java.util.concurrent.locks. Zapewnia takie samo wzajemne wykluczanie jak synchronized, ale daje większą kontrolę. Blokadę należy zwolnić ręcznie w bloku finally.

import java.util.concurrent.locks.*;

class SafeCounter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock(); // always release in finally!
        }
    }
    
    int get() {
        lock.lock();
        try { return count; }
        finally { lock.unlock(); }
    }
}

tryLock: nieblokujące uzyskiwanie blokady

tryLock() próbuje uzyskać blokadę bez blokowania wątku. Zwraca true, jeśli się to powiedzie, lub false, jeśli blokada jest zajęta przez inny wątek.

ReentrantLock lock = new ReentrantLock();

if (lock.tryLock()) {
    try {
        System.out.println("Got the lock, doing work");
    } finally {
        lock.unlock();
    }
} else {
    System.out.println("Lock busy, skipping or retrying");
}

tryLock z limitem czasu

Oczekiwanie przez określony czas na uzyskanie blokady:

try {
    if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
        try {
            // work with protected resource
            System.out.println("Acquired within 500ms");
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("Timed out waiting for lock");
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

lockInterruptibly

lockInterruptibly() uzyskuje blokadę, chyba że wątek zostanie przerwany — jest to przydatne w przypadku anulowalnych zadań oczekujących na blokadę:

try {
    lock.lockInterruptibly(); // throws InterruptedException if interrupted
    try {
        // do work
    } finally {
        lock.unlock();
    }
} catch (InterruptedException e) {
    System.out.println("Interrupted while waiting for lock");
    Thread.currentThread().interrupt();
}

Reentrantność

Zarówno synchronized, jak i ReentrantLock są reentrantne — wątek, który już posiada blokadę, może uzyskać ją ponownie bez zakleszczenia:

synchronized void outer() {
    System.out.println("outer");
    inner(); // same thread re-enters — OK
}

synchronized void inner() {
    System.out.println("inner"); // same lock, same thread
}

// ReentrantLock:
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock(); // acquire again — hold count = 2
lock.unlock(); // hold count = 1
lock.unlock(); // hold count = 0, lock released

Zasada sprawiedliwego dostępu

Należy utworzyć sprawiedliwą blokadę ReentrantLock, aby przyznawać dostęp w kolejności FIFO — zapobiega to zagłodzeniu wątków kosztem mniejszej przepustowości:

// Unfair (default): no ordering guarantee, better throughput
ReentrantLock unfair = new ReentrantLock();

// Fair: threads acquire in arrival order
ReentrantLock fair = new ReentrantLock(true);

System.out.println(fair.isFair()); // true

Zmienne warunkowe

ReentrantLock udostępnia obiekty Condition — bardziej elastyczne niż wait()/notify():

ReentrantLock lock2 = new ReentrantLock();
Condition notEmpty = lock2.newCondition();
Queue<String> queue = new LinkedList<>();

// Producer
lock2.lock();
try {
    queue.offer("item");
    notEmpty.signal(); // wake one waiting thread
} finally { lock2.unlock(); }

// Consumer
lock2.lock();
try {
    while (queue.isEmpty()) notEmpty.await(); // wait & release lock
    System.out.println(queue.poll());
} finally { lock2.unlock(); }

Kiedy używać ReentrantLock, a kiedy synchronized

Należy użyć synchronized, gdy:

  • potrzebne jest proste wzajemne wykluczanie
  • nie są wymagane limit czasu, sprawiedliwy dostęp ani możliwość przerwania

Należy użyć ReentrantLock, gdy:

  • potrzebne jest tryLock() lub tryLock(timeout)
  • potrzebne jest lockInterruptibly()
  • wymagana jest sprawiedliwa kolejność
  • potrzebnych jest wiele zmiennych Condition

Licznik blokady i monitorowanie

ReentrantLock udostępnia metody diagnostyczne:

ReentrantLock lock3 = new ReentrantLock();
lock3.lock();
lock3.lock(); // reentrant — hold count 2

System.out.println(lock3.getHoldCount());    // 2
System.out.println(lock3.isHeldByCurrentThread()); // true
System.out.println(lock3.isLocked());        // true
System.out.println(lock3.getQueueLength());  // 0 (no waiting threads)

lock3.unlock();
lock3.unlock(); // fully released

Częsty błąd: odblokowanie bez wcześniejszego zablokowania

Wywołanie unlock(), gdy bieżący wątek nie posiada blokady, powoduje zgłoszenie wyjątku IllegalMonitorStateException. Należy zawsze parować lock z unlock albo używać try-finally:

ReentrantLock lock4 = new ReentrantLock();
try {
    // If lock() is never called (e.g., skipped by exception before this line)
    lock4.unlock(); // throws IllegalMonitorStateException
} catch (IllegalMonitorStateException e) {
    System.out.println("Must lock before unlock!");
}

Szybkie sprawdzenie

Która metoda ReentrantLock pozwala wątkowi uzyskać blokadę albo natychmiast powrócić bez blokowania, jeśli blokada jest niedostępna?

Podsumowanie: ReentrantLock a synchronized

Najważniejsze informacje:

  • Oba rozwiązania zapewniają wzajemne wykluczanie i reentrantność
  • ReentrantLock dodatkowo oferuje: tryLock, lockInterruptibly, sprawiedliwy dostęp i obiekty Condition
  • Blokadę należy zawsze zwalniać w bloku finally
  • synchronized jest prostsze — należy je preferować, gdy jego funkcje są wystarczające
  • ReentrantLock sprawdza się w zaawansowanych scenariuszach: z limitami czasu, możliwością przerwania i wieloma zmiennymi warunkowymi

Często zadawane pytania

Czy lekcja „ReentrantLock a synchronized” jest bezpłatna?

Tak — pełny tekst „ReentrantLock a synchronized” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „ReentrantLock a synchronized”?

Porównuj funkcje ReentrantLock (tryLock, lockInterruptibly, fairness) ze słowem kluczowym synchronized. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „ReentrantLock a synchronized”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. ReentrantLock a synchronized
  2. ReadWriteLock w scenariuszach czytelnik-pisarz
  3. Zmienne atomowe: aktualizacje bez blokad
  4. StampedLock i optymistyczne odczyty
← Powrót do Java Academy