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 releasedZasada 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()); // trueZmienne 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 releasedCzę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
- ReentrantLock a synchronized
- ReadWriteLock w scenariuszach czytelnik-pisarz
- Zmienne atomowe: aktualizacje bez blokad
- StampedLock i optymistyczne odczyty