Syntax and Application
The synchronized keyword integrates directly into the Java language syntax, applicable to entire methods or specific code boundaries. Conversely, ReentrantLock operates as a concrete class within the java.util.concurrent.locks package, demanding object instantiation and explicit invocations of lock() and unlock().
Lock Lifecycle Management
Implicit handling characterizes synchronized; the compiler automatically generates bytecode to acquire and release the monitor. ReentrantLock mandates explicit lifecycle control, shifting the burden of ensuring locks are released—typically via a finally block—onto the developer.
Fairness Policies
A strictly non-fair strategy is enforced by synchronized, meaning waiting threads are not guaranteed acquisition order. ReentrantLock defaults to non-fair behavior but accepts a boolean constructor argument to enforce fairness, ensuring longest-waiting threads acquire the lock first.
public class TicketVault {
private int availableTickets = 50;
public synchronized void reserveTicket() {
availableTickets--;
}
public synchronized int getAvailableTickets() {
return availableTickets;
}
}
public class ConcurrentTicketVault {
private final ReentrantLock mutex = new ReentrantLock(true);
private int availableTickets = 50;
public void reserveTicket() {
mutex.lock();
try {
availableTickets--;
} finally {
mutex.unlock();
}
}
public int getAvailableTickets() {
mutex.lock();
try {
return availableTickets;
} finally {
mutex.unlock();
}
}
}
Interrupt Handling
Threads blocked waiting for a synchronized monitor cannot respond to interruptions; they persist until the lock is obtained. ReentrantLock offers the lockInterruptibly() method, which throws an InterruptedException if the thread is signaled to stop while waiting.
Architectural Foundation
The JVM's native monitor mechanism underpins synchronized. ReentrantLock relies on the AbstractQueuedSynchronizer (AQS) framework, facilitating advanced capabilities like multiple Condition instances for targeted thread signaling.
Throughput and Optimization
Historically, ReentrantLock outperformed synchronized under heavy contention. Extensive JVM enhancements—such as adaptive spinning and lock coarsening—have largely equalized synchronized performance, making the choice dependent on feature requirements rather than raw speed.
Failure Modes
If an exception propagates out of a synchronized block, the JVM automatically frees the intrinsic lock. With ReentrantLock, neglecting to call unlock() inside a finally block during an exception will permanently hold the lock, risking deadlocks.
Non-blocking Acquisition
ReentrantLock exposes the tryLock() API, enabling attempts to acquire the lock without indefinite blocking, including overloaded variants with timeout constraints—a capability absent in intrinsic locks.