Back to Locks: ReentrantLock, ReadWriteLock, StampedLock
Q4Question 4 of 7

What is ReadWriteLock?

Key Concepts
  • ReentrantReadWriteLock provides a shared read lock (multiple concurrent readers) and an exclusive write lock (blocks all readers and other writers).
  • It dramatically improves throughput when reads outnumber writes (95%+ reads) because readers run in parallel instead of serializing.
  • Under write-heavy workloads (50:50), the overhead of maintaining a shared reader count via CAS makes it slower than a simple ReentrantLock.
  • Write lock can be downgraded to read lock (allowed), but read lock cannot be upgraded to write lock (deadlock).
  • Fair mode (FIFO) prevents writer starvation but reduces throughput; non-fair mode (default) allows barging.
  • The lock is reentrant: the same thread can acquire the read lock or write lock multiple times.
  • For 99:1 read:write ratios, prefer StampedLock optimistic read for even better performance.

Definition

ReentrantReadWriteLock allows multiple concurrent readers or one exclusive writer, dramatically improving throughput for read-heavy workloads but adding overhead for write-heavy ones due to the shared reader-count CAS.
  • Read lock (shared) – multiple threads can hold the read lock simultaneously. Each readLock() call increments a shared reader count via CAS.
  • Write lock (exclusive) – only one thread can hold the write lock, and it blocks all readers and other writers.
  • Read-heavy benefit – when 95%+ operations are reads, 100 readers run in parallel instead of serializing through one lock. Throughput increases dramatically.
  • Write-heavy cost – frequent write locks block all readers, and the CAS overhead on the shared reader counter adds contention between readers. In a 50:50 workload, a simple ReentrantLock is often faster.
  • Downgrade – a thread holding the write lock can acquire the read lock and then release the write lock, downgrading to a read lock. This allows the thread to continue reading while letting other readers in.
  • No upgrade – acquiring the write lock while holding the read lock causes deadlock because the thread blocks waiting for readers to release, but it is itself a reader.

ReentrantLock is like one door to National Bank of India's vault: only one person at a time, whether they are reading or writing. ReadWriteLock is like a wide door with a sign: multiple people can enter to read the register simultaneously, but when someone needs to write, everyone else must leave until the writing is done. If 95% of visitors are readers, the wide door handles 100 people at once instead of one at a time. But if 50% are writers, the constant clearing and re-filling of the room creates more overhead than a simple one-person door.

Understanding the Concepts

What is ReadWriteLock?

ReentrantReadWriteLock separates read operations from write operations to allow higher concurrency for read-heavy workloads. Multiple threads can hold the read lock simultaneously, but the write lock is exclusive: it blocks all readers and other writers.

JAVA
package com.bank.concurrency;

import java.math.BigDecimal;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class BalanceCacheRWL {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private BigDecimal balance = new BigDecimal("100000.00");

    public BigDecimal getBalance() {
        rwl.readLock().lock();
        try {
            return balance;  // 100 threads can be here simultaneously
        } finally {
            rwl.readLock().unlock();
        }
    }

    public void updateBalance(BigDecimal newBalance) {
        rwl.writeLock().lock();
        try {
            balance = newBalance;  // only ONE thread, all readers blocked
        } finally {
            rwl.writeLock().unlock();
        }
    }
}

When does ReadWriteLock help?

It helps when reads vastly outnumber writes. With a simple ReentrantLock, 100 read operations serialize: one at a time. With ReadWriteLock, all 100 run in parallel because the read lock is shared.

When does ReadWriteLock hurt?

When writes are frequent (50:50), the write lock constantly blocks all readers, and the CAS overhead on the shared reader count adds contention even between readers. A simple ReentrantLock has lower per-operation overhead and performs better.

How does write-to-read downgrade work?

JAVA
package com.bank.concurrency;

import java.math.BigDecimal;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class DowngradeDemo {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private BigDecimal balance = BigDecimal.ZERO;

    public BigDecimal updateAndRead(BigDecimal amount) {
        rwl.writeLock().lock();
        try {
            balance = balance.add(amount);
            rwl.readLock().lock();   // acquire read WHILE holding write
        } finally {
            rwl.writeLock().unlock();  // release write, now hold only read
        }
        try {
            return balance;  // still holding read lock, other readers can join
        } finally {
            rwl.readLock().unlock();
        }
    }
}

Why is read-to-write upgrade not allowed?

JAVA
// WRONG: read-to-write upgrade causes DEADLOCK
rwl.readLock().lock();
rwl.writeLock().lock();  // DEADLOCK!
// Write lock waits for ALL readers to release.
// But THIS thread is a reader and will not release until it gets the write lock.
// Circular dependency: deadlock.

The correct approach is to release the read lock first, acquire the write lock, then re-check the condition.

How do you choose between lock types?

| Workload | Recommended lock | Reason |
|---|---|---|
| 99:1 read:write | StampedLock optimistic | Zero CAS, zero contention |
| 95:5 read:write | ReentrantReadWriteLock | Parallel reads, occasional write blocking |
| 50:50 | ReentrantLock | Simplest, lowest overhead |
| Low contention | synchronized | JVM optimizes heavily |

What about fairness?

Fair mode (new ReentrantReadWriteLock(true)) uses FIFO ordering: the longest-waiting thread gets the lock next. This prevents writer starvation (where a continuous stream of readers prevents writers from ever acquiring the lock). Non-fair mode (default) allows barging, where a thread may acquire the lock ahead of waiting threads. Barging improves throughput but can starve individual threads.

What are common interview follow-up questions?

  • Can you hold both read and write locks at the same time? Yes, during downgrade: hold write lock, acquire read lock, release write lock. But NOT during upgrade.
  • Is ReadWriteLock better than ConcurrentHashMap? ConcurrentHashMap uses per-bucket locking which is usually sufficient. ReadWriteLock is for custom data structures or cross-structure operations.

Visual Overview

DIAGRAM
  ReadWriteLock: Shared Readers, Exclusive Writer
  ═══════════════════════════════════════════════

  Time →
  ┌──────────────────────────────────────┐
  │ readLock: R1  R2  R3  R4  R5        │  5 readers in parallel
  └──────────────────────────────────────┘
  ┌──────┐
  │ W1   │  writeLock: exclusive, readers blocked
  └──────┘
  ┌──────────────────────────────────────┐
  │ readLock: R6  R7  R8  R9  R10       │  readers resume
  └──────────────────────────────────────┘

  Downgrade (allowed):
  writeLock() → readLock() → writeUnlock() → (now read only)

  Upgrade (DEADLOCK):
  readLock() → writeLock() → DEADLOCK! (thread waits for itself)

Key Takeaway

ReentrantReadWriteLock provides a shared read lock and an exclusive write lock, dramatically improving throughput for read-heavy workloads where 95% or more operations are reads. Multiple readers run in parallel instead of serializing through a single lock. Under write-heavy workloads, the CAS overhead on the shared reader count and the constant reader-blocking by writers make it slower than a simple ReentrantLock.

The lock supports write-to-read downgrade (acquire read lock while holding write lock, then release write lock) but does NOT support read-to-write upgrade, which causes deadlock. Fair mode prevents writer starvation but reduces throughput.

At National Bank of India, a balance cache serving 10,000 reads per second and 50 writes per second achieved P99 latency of 0.2 milliseconds with ReadWriteLock, compared to 5 milliseconds with ReentrantLock. For even higher read-to-write ratios, StampedLock's optimistic read eliminates the CAS contention entirely.

Remember one sentence: ReadWriteLock lets multiple readers proceed in parallel while writers get exclusive access, making it ideal for read-heavy data but costly for write-heavy workloads.

---

Share
Use ← → arrow keys to navigate