Synchronized vs ReentrantLock in Java: Key Differences and Usage
synchronized is a built-in keyword that provides simple mutual exclusion with automatic lock release, while ReentrantLock is a flexible class from java.util.concurrent.locks offering advanced features like timed waits and interruptible lock acquisition. ReentrantLock allows more control but requires manual unlocking, unlike synchronized which is easier to use but less flexible.Quick Comparison
This table summarizes the main differences between synchronized and ReentrantLock in Java.
| Feature | synchronized | ReentrantLock |
|---|---|---|
| Type | Keyword | Class from java.util.concurrent.locks |
| Lock Release | Automatic (block exit) | Manual (must call unlock()) |
| Lock Acquisition | Non-interruptible | Supports interruptible and timed lock attempts |
| Fairness | No fairness option | Can be fair (first-come-first-served) |
| Condition Support | No explicit condition variables | Supports multiple Condition objects |
| Performance | Simpler, less overhead | More flexible, slightly more overhead |
Key Differences
synchronized is a simple and easy-to-use keyword that locks a block or method and automatically releases the lock when the block finishes or an exception occurs. It does not support interruptible lock acquisition or fairness policies, making it less flexible but safe and straightforward for basic synchronization needs.
ReentrantLock is a class that provides explicit lock control with methods like lock(), unlock(), tryLock(), and supports interruptible locking and timed waits. It also allows fairness policies to avoid thread starvation and supports multiple Condition objects for advanced thread coordination.
Because ReentrantLock requires manual unlocking, it demands careful coding to avoid deadlocks or forgotten unlocks, while synchronized handles this automatically. The choice depends on whether you need advanced features or prefer simplicity.
Code Comparison
Here is an example showing how to use synchronized to protect a shared counter increment.
public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + counter.getCount()); } }
ReentrantLock Equivalent
This example shows the same counter increment using ReentrantLock for locking.
import java.util.concurrent.locks.ReentrantLock; public class Counter { private int count = 0; private final ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount() { lock.lock(); try { return count; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + counter.getCount()); } }
When to Use Which
Choose synchronized when you want simple, safe locking with automatic release and do not need advanced features like timed or interruptible locks. It is perfect for straightforward synchronization in most cases.
Choose ReentrantLock when you need more control over locking, such as fairness policies, interruptible lock acquisition, timed waits, or multiple condition variables for complex thread coordination. It is ideal for advanced concurrency scenarios where flexibility is required.
Key Takeaways
synchronized is simpler and automatically releases locks, best for basic synchronization.ReentrantLock offers advanced features like timed, interruptible locks and fairness control.ReentrantLock in a finally block to avoid deadlocks.synchronized for ease and safety; use ReentrantLock for flexibility and advanced control.