Java Concurrency

What is the difference between ReentrantLock and synchronized?

synchronized is Java's built-in locking mechanism. ReentrantLock gives similar mutual exclusion but adds more control, such as tryLock, timed waits, interruptible locking, fairness, and explicit unlock behavior.

ConcurrencyLocksSenior Java

The Short Answer

synchronizedis Java's built-in locking mechanism. It is simple, automatic, and great when you just need to protect a critical section.

ReentrantLock gives you more control: timed lock attempts, interruptible waiting, optional fairness, and explicit locking/unlocking. It has the same basic mutual-exclusion idea as synchronized, but with extended capabilities.

ReentrantLock is not “better synchronized.” It is synchronized with more knobs and more responsibility.

The Mental Model

synchronized

Automatic Door Lock

Enter block → lock acquired
Exit block → lock released

Java handles lock release automatically when the synchronized block exits.

ReentrantLock

Manual Industrial Lock

lock.lock()
try / finally
lock.unlock()

You control when the lock is acquired and released. That gives flexibility, but forgetting unlock can break the program.

Basic Code Comparison

With synchronized method:

java
private int count = 0;

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

Or with a synchronized block:

java
private final Object lock = new Object();
private int count = 0;

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

With ReentrantLock:

java
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;

public void increment() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}
With ReentrantLock, the finally block is not optional. If an exception happens and unlock is skipped, other threads may wait forever.

Why ReentrantLock Exists

If synchronized already works, why have ReentrantLock? Because some systems need more control than “wait until the lock is available.”

Timeout

Try to get the lock, but give up after a limit.

Interruptible Wait

Let a waiting thread be interrupted instead of waiting forever.

Fairness

Optionally give waiting threads a more queue-like ordering.

tryLock: The Big Practical Difference

With synchronized, if a thread reaches the lock, it waits until it can enter.

With ReentrantLock, you can ask: “Can I get the lock right now?” or “Can I get it within two seconds?”

java
if (lock.tryLock()) {
    try {
        // critical section
    } finally {
        lock.unlock();
    }
} else {
    // lock was busy, do something else
}
Important nuance on fairness: the non-timed tryLock() method does not honor fairness. If the lock happens to be available, a newly arriving thread may acquire it immediately even if other threads have been waiting longer.
java
if (lock.tryLock(2, TimeUnit.SECONDS)) {
    try {
        // critical section
    } finally {
        lock.unlock();
    }
} else {
    // could not acquire lock within timeout
}

The timed version of tryLock allows a thread to wait for a limited amount of time before giving up. This is useful when waiting forever would be undesirable or dangerous.

Timeout Locking

Thread A holds lock
synchronized: Thread B waits
tryLock: Thread B can give up gracefully

What Does Reentrant Mean?

Reentrant means the same thread can acquire the same lock more than once without deadlocking itself.

java
lock.lock();
try {
    // same thread can acquire again
    lock.lock();
    try {
        // nested protected work
    } finally {
        lock.unlock();
    }
} finally {
    lock.unlock();
}

The lock keeps a hold count. If the same thread locks twice, it must unlock twice before another thread can acquire it.

Fairness

ReentrantLock can be created as fair:

java
Lock lock = new ReentrantLock(true);

A fair lock tries to grant access to the longest-waiting thread. This can reduce starvation, but it may reduce throughput because the system gives up some scheduling flexibility. Fair locks are usually more predictable, but often slightly slower than the default non-fair behavior.

Fair vs Non-Fair Lock

Fair

Thread B waiting
Thread C waiting
B usually goes first

Non-Fair

Thread B waiting
Thread C arrives
Scheduler may allow barging

Money Transfer Example

This connects directly to the account transfer problem. If you need to lock two accounts, you still need consistent lock ordering to avoid deadlock.

java
public void transfer(Account from, Account to, int amount) {
    Account first = from.id() < to.id() ? from : to;
    Account second = from.id() < to.id() ? to : from;

    first.lock().lock();
    try {
        second.lock().lock();
        try {
            from.withdraw(amount);
            to.deposit(amount);
        } finally {
            second.lock().unlock();
        }
    } finally {
        first.lock().unlock();
    }
}

This is the ReentrantLock version of nested synchronized blocks. The idea is the same: acquire locks in a consistent order so two threads do not grab opposite locks and wait forever.

Interview-Friendly Explanation

synchronized is best when I need simple, scoped mutual exclusion and want Java to release the lock automatically. ReentrantLock is useful when I need more control, such as tryLock, timeout, interruptible waiting, fairness, or multiple Condition objects. The tradeoff is that I must release the lock manually in a finally block.

When to Use Which

Use synchronized when...

  • The critical section is simple.
  • You do not need timeout behavior.
  • You do not need fairness control.
  • You want safer automatic lock release.

Use ReentrantLock when...

  • You need tryLock.
  • You need timed lock attempts.
  • You need interruptible lock waits.
  • You need fairness or Condition objects.

Common Interview Follow-Ups

Is ReentrantLock faster than synchronized?

Not automatically. Modern JVMs have optimized synchronized heavily. Choose based on needed behavior first, not assumed speed.

Can synchronized do tryLock?

No. synchronized waits until the monitor is available. ReentrantLock gives tryLock and timed tryLock.

What is the biggest danger of ReentrantLock?

Forgetting to unlock. That is why unlock belongs in a finally block.

What does fair locking mean?

A fair ReentrantLock tries to favor the longest-waiting thread, reducing starvation risk but often lowering throughput.

Final Takeaway

synchronized is simple and safe for common locking. ReentrantLock is for cases where you need explicit control over how waiting, timeout, interruption, or fairness should behave.