Back to Locks: ReentrantLock, ReadWriteLock, StampedLock
Q7Question 7 of 7

What is lock downgrading?

Key Concepts
  • Downgrade: acquire read lock before releasing write lock to move from exclusive to shared access safely.
  • Downgrading is explicitly supported because the write lock is exclusive, so acquiring the read lock succeeds immediately.
  • Upgrading (read to write) is NOT supported: two readers both upgrading deadlock waiting for each other.
  • Without downgrading, releasing write then separately acquiring read leaves a race window for another writer.
  • Downgrading guarantees continuity of protection: you read the exact value you just wrote.
  • The workaround for upgrade: release read, acquire write, and re-check the condition.
  • Useful when a thread must write a value and immediately read the fresh derived result.

Definition

Downgrade safely by taking the read lock before releasing the write lock; never try to upgrade read to write because two readers both upgrading deadlock, which is why the JVM does not support it.
  • Downgrade – while holding the write lock, acquire the read lock, then release the write lock; moves from exclusive to shared access.
  • Safe – because the write lock is exclusive, no one else holds any lock, so the read lock succeeds immediately.
  • Upgrade forbidden – if two readers both try to upgrade, each waits for the other to release its read lock, causing deadlock.
  • Race window – without downgrading, releasing write then acquiring read separately lets another writer intervene.
  • Workaround – for upgrade: release read, acquire write, re-check the condition.

Picture the National Bank of India master interest-rate sheet. The write lock is locking yourself alone in the records room. Downgrading is propping the door open to reading mode after changing the rate, so you read the exact value you just wrote. Upgrading is two auditors both reading in the room both wanting to become the sole editor: each refuses to leave until the other leaves, so they stand forever.

Understanding the Concepts

What is lock downgrading?

A ReentrantReadWriteLock exposes readLock() (shared, multiple readers) and writeLock() (exclusive, one writer). The holder of the write lock can also acquire the read lock because the lock is reentrant and the write lock implies exclusive access.

How do you downgrade safely?

The key is to acquire the read lock before releasing the write lock. This guarantees continuous protection with no gap.

JAVA
package com.bank.treasury;

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

public class InterestRateService {

    private final ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private BigDecimal rate = new BigDecimal("0.065");

    public BigDecimal updateAndApplyRate(BigDecimal newRate, BigDecimal principal) {
        rw.writeLock().lock();                 // Step 1: exclusive access
        try {
            rate = newRate;                    // Step 2: WRITE the new rate
            rw.readLock().lock();              // Step 3: acquire read BEFORE releasing write
        } finally {
            rw.writeLock().unlock();           // Step 4: release write lock (downgrade complete)
        }
        try {
            // Step 5: read the fresh value under read lock protection
            return principal.multiply(rate);
        } finally {
            rw.readLock().unlock();            // Step 6: release read lock
        }
    }
}

Why is upgrading forbidden?

If a thread holds the read lock and calls writeLock().lock(), it blocks because the write lock needs exclusivity, but the thread itself still holds a read lock that prevents exclusivity. With two threads, both upgrading creates a deadlock.

JAVA
// DEADLOCK SCENARIO:
// Thread A: readLock held → writeLock.lock() → blocks (waiting for B's read)
// Thread B: readLock held → writeLock.lock() → blocks (waiting for A's read)
// Neither can release their read lock because they are both stuck in writeLock.lock()

rw.readLock().lock();
try {
    rw.writeLock().lock();   // BLOCKS FOREVER: waiting for own read lock release
} finally { /* never reached */ }

What is the upgrade workaround?

Release the read lock, acquire the write lock, and re-check the condition because the state may have changed during the gap.

JAVA
package com.bank.treasury;

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

public class ConditionalRateUpdate {

    private final ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private BigDecimal rate = new BigDecimal("0.065");

    public BigDecimal conditionalUpdate(BigDecimal newRate) {
        rw.readLock().lock();
        try {
            if (needsUpdate(rate, newRate)) {
                rw.readLock().unlock();        // release read first
                rw.writeLock().lock();         // acquire write (gap exists here!)
                try {
                    if (needsUpdate(rate, newRate)) {   // RE-CHECK: state may have changed
                        rate = newRate;
                    }
                    rw.readLock().lock();      // downgrade to read
                } finally {
                    rw.writeLock().unlock();   // now only read lock held
                }
            }
            return rate;
        } finally {
            rw.readLock().unlock();
        }
    }

    private boolean needsUpdate(BigDecimal current, BigDecimal proposed) {
        return current.compareTo(proposed) != 0;
    }
}

Why is downgrading useful?

It closes the race window. Without downgrading, releasing write then separately acquiring read leaves a gap where another writer can change the value.

| Approach | Race Window? | Continuity? |
|----------|-------------|-------------|
| Downgrade (read before releasing write) | No gap | Read the exact value you wrote |
| Release write, then acquire read | Gap exists | Another writer may intervene |

How does StampedLock improve on this?

StampedLock (Java 8) provides tryConvertToReadLock(stamp) and tryConvertToWriteLock(stamp) methods that attempt atomic conversion. If the conversion fails, you fall back to the release-and-reacquire pattern.

JAVA
StampedLock sl = new StampedLock();
long stamp = sl.writeLock();
try {
    rate = newRate;
    long readStamp = sl.tryConvertToReadLock(stamp);  // atomic downgrade
    if (readStamp != 0L) {
        stamp = readStamp;  // downgrade succeeded
    } else {
        sl.unlockWrite(stamp);  // fall back: release write, acquire read
        stamp = sl.readLock();
    }
    return principal.multiply(rate);
} finally {
    sl.unlock(stamp);
}

What are common interview follow-up questions?

  • How does StampedLock handle optimistic reads and upgrades? Optimistic read uses tryOptimisticRead() which returns a stamp without acquiring any lock. Validate with validate(stamp) before using the data.
  • Can you downgrade from a StampedLock write stamp to a read stamp? Yes, with tryConvertToReadLock(writeStamp).
  • What pattern replaces upgrade in practice? The release-recheck-acquire pattern shown above is the standard workaround.

Visual Overview

DIAGRAM
  Downgrade (Safe):
  ┌───────────────────────────────────────┐
  │ 1. writeLock().lock()    [exclusive]   │
  │ 2. mutate state                        │
  │ 3. readLock().lock()     [still excl]  │
  │ 4. writeLock().unlock()  [shared only] │
  │ 5. read derived value    [protected]   │
  │ 6. readLock().unlock()   [released]    │
  └───────────────────────────────────────┘

  Upgrade (FORBIDDEN):
  Thread A: readLock held → writeLock.lock() → BLOCKED (own read)
  Thread B: readLock held → writeLock.lock() → BLOCKED (A's read)
  Result: DEADLOCK

  Workaround:
  readLock.unlock() → writeLock.lock() → re-check → readLock.lock() → writeLock.unlock()

Key Takeaway

Lock downgrading is the safe pattern where a thread holding the write lock acquires the read lock before releasing the write lock, moving from exclusive to shared access with no race window. Upgrading (read to write) is forbidden because two readers both upgrading deadlock.

At National Bank of India, treasury used downgrading to update the interest rate and compute the derived daily factor from the exact new value, guaranteeing no other writer slipped in between.

Remember one sentence: downgrade by taking the read lock before releasing the write lock, and never upgrade read to write because two readers both upgrading will deadlock.

---

Share
Use ← → arrow keys to navigate