BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Concurrency

Concurrency

ReentrantLock and ReadWriteLock

The java.util.concurrent.locks classes are explicit versions of synchronized with extras: try without waiting, time out, separate read and write access, and more than one condition.

Learn these first

At a glance

Mental model

A key you pick up and put back yourself instead of a door that locks automatically, so you must never forget to hand it back.

What happens, in order
  1. 1lock.lock(); try { ..} finally { lock.unlock(); }: the finally is mandatory, otherwise an exception leaves the lock held forever.
  2. 2tryLock() or tryLock(timeout, unit) returns false instead of waiting indefinitely, which helps avoid deadlock
  3. 3ReadWriteLock allows many readers or one writer, which pays off when reads dominate
  4. 4CountDownLatch, Semaphore and CyclicBarrier coordinate threads: wait for N events, limit concurrent access to N, or make a group meet at a point
Remember
Watch out
unlock outside finallyLocking in one method and unlocking in anotherReader lock then trying to upgrade to writer (deadlocks)
Example (highlighted lines explain it)
private final ReentrantLock lock = new ReentrantLock();public void transfer(Account from, Account to, long amount) {    lock.lock();    try {        from.withdraw(amount);        to.deposit(amount);    } finally {        lock.unlock();      // always released, even after an exception    }}
  1. line 1 uses ReentrantLock, the core of ReentrantLock and ReadWriteLock
  2. line 4 uses lock, the core of ReentrantLock and ReadWriteLock
  3. line 9 always released, even after an exception
Read the full explanation (about 1 min)

The java.util.concurrent.locks classes are explicit versions of synchronized with extras: try without waiting, time out, separate read and write access, and more than one condition.

A mental model

A key you pick up and put back yourself instead of a door that locks automatically, so you must never forget to hand it back.

How it works

lock.lock(); try { ... } finally { lock.unlock(); }: the finally is mandatory, otherwise an exception leaves the lock held forever.

tryLock() or tryLock(timeout, unit) returns false instead of waiting indefinitely, which helps avoid deadlock.

ReadWriteLock allows many readers or one writer, which pays off when reads dominate.

CountDownLatch, Semaphore and CyclicBarrier coordinate threads: wait for N events, limit concurrent access to N, or make a group meet at a point.

Going deeper

Prefer synchronized when it is enough; use explicit locks when you need their features. A forgotten unlock is a hang that only appears under load. A Semaphore is a good way to cap concurrent calls to a limited resource.

Under the hood

These are built on AbstractQueuedSynchronizer: an atomic state integer plus a queue of waiting threads, using compare-and-swap and LockSupport.park.

Common mistakes

  • unlock outside finally.
  • Locking in one method and unlocking in another.
  • Reader lock then trying to upgrade to writer (deadlocks).

How Unravel recognises it

When you paste code, Unravel looks for these signs of reentrantlock and readwritelock. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Builds on this

Frequently asked questions

What is ReentrantLock and ReadWriteLock?

The java.util.concurrent.locks classes are explicit versions of synchronized with extras: try without waiting, time out, separate read and write access, and more than one condition.

What are the most common mistakes with ReentrantLock and ReadWriteLock?

unlock outside finally. Locking in one method and unlocking in another. Reader lock then trying to upgrade to writer (deadlocks).

How can I see ReentrantLock and ReadWriteLock in real code?

Paste your own Java into Unravel and it will point out where reentrantlock and readwritelock appears, explain how it works at the detail level you choose, and draw the flow of the code around it.