ReadWriteLock & StampedLock in Java

12 Jul 2026 5 min read
1
Using a traditional mutual exclusion lock such as synchronized or ReentrantLock forces every thread—whether reading or writing—to execute one at a time.

Although this guarantees thread safety, it unnecessarily reduces concurrency because multiple readers could safely access the same data simultaneously.

To address this problem, Java provides the ReadWriteLock interface in the java.util.concurrent.locks package.

Unlike a traditional lock, a ReadWriteLock maintains two separate locks: one for reading and another for writing.

This allows multiple threads to read shared data concurrently while still ensuring exclusive access during write operations.

ReadWriteLock

The ReadWriteLock interface defines two separate locks. Instead of protecting all operations with a single lock, developers explicitly choose the appropriate lock depending on whether the operation reads or modifies shared data.

The most commonly used implementation is:
private final ReadWriteLock lock = new ReentrantReadWriteLock();
1. Multiple threads can hold the read lock simultaneously, provided no thread holds the write lock.
2. Only one thread can hold the write lock at a time.
3. While a thread holds the write lock, no other thread can acquire either the read lock or the write lock.
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class ProductCache {

    private String product = "Laptop";
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    public String getProduct() {

        lock.readLock().lock();

        try {
            return product;
        } finally {
            lock.readLock().unlock();
        }
    }

    public void updateProduct(String newProduct) {

        lock.writeLock().lock();

        try {
            product = newProduct;
        } finally {
            lock.writeLock().unlock();
        }
    }
}
Here, multiple threads can execute getProduct() simultaneously because they acquire the read lock.

However, when updateProduct() executes, it acquires the write lock, preventing both readers and other writers from accessing the shared data until the update completes.

StampedLock

While ReadWriteLock significantly improves performance in read-heavy applications by allowing multiple readers to access shared data simultaneously, it still requires every read operation to acquire and release a lock.

Even though multiple readers can execute concurrently, lock acquisition itself introduces some overhead. In applications where writes are extremely rare but reads occur thousands or even millions of times per second, this overhead can become noticeable.

To further optimize such scenarios, Java 8 introduced the StampedLock class in the java.util.concurrent.locks package. Unlike ReentrantLock and ReadWriteLock, a StampedLock supports three locking modes: write locking, read locking, and optimistic reading.

The optimistic read mode allows threads to read shared data without acquiring a traditional lock, making read operations significantly faster when write operations are infrequent.

Instead of simply locking and unlocking, each locking operation in a StampedLock returns a long value representing the lock stamp.
StampedLock lock = new StampedLock();
long stamp = lock.writeLock();
The same stamp must later be supplied when releasing the lock.
lock.unlockWrite(stamp); 
The stamp uniquely identifies the lock acquisition and helps the lock verify ownership when it is released.

Three Locking Modes

Unlike other locking mechanisms, StampedLock supports three different modes of operation.

1. Write Lock: Provides exclusive access for modifying shared data.
2. Read Lock: Allows multiple readers to access shared data simultaneously, similar to ReadWriteLock.
3. Optimistic Read: Allows reading shared data without acquiring a traditional lock, provided no write occurs during the read operation.

This optimistic mode is what makes StampedLock unique.

Write Lock

A write lock behaves similarly to the write lock of ReadWriteLock. Only one thread can hold it at a time.
import java.util.concurrent.locks.StampedLock;

class Product {

    private String name = "Laptop";
    private final StampedLock lock = new StampedLock();

    public void update(String value) {

        long stamp = lock.writeLock();

        try {
            name = value;
        } finally {
            lock.unlockWrite(stamp);
        }
    }
}
During the write operation, all readers and other writers must wait until the write lock is released.

Read Lock

The read lock functions similarly to the read lock of ReadWriteLock.
public String getName() {

    long stamp = lock.readLock();

    try {
        return name;
    } finally {
        lock.unlockRead(stamp);
    }
}
Multiple threads can hold the read lock simultaneously, provided no thread currently owns the write lock.

Optimistic Read

Instead of acquiring a read lock, the thread simply assumes that no write operation will occur while it is reading the data.
long stamp = lock.tryOptimisticRead(); 
This operation does not block and does not acquire a traditional lock. It simply returns a stamp representing the current state of the lock. The thread can now read shared variables normally.

long stamp = lock.tryOptimisticRead(); 
String currentName = name; 
double currentPrice = price; 
Since no actual lock was acquired, another thread might modify the data while it is being read. Therefore, the optimistic read must always be validated.

After reading the shared data, the thread verifies whether a write occurred during the read operation.
if (lock.validate(stamp)) {
    System.out.println("Read is valid");
} else {
    System.out.println("Data changed");
}
If validate() returns true, no write occurred and the data can be safely used.

If it returns false, another thread modified the data while it was being read. In that case, the optimistic read must be discarded and repeated using a normal read lock.
import java.util.concurrent.locks.StampedLock;

class Product {

    private String name = "Laptop";
    private final StampedLock lock = new StampedLock();

    public String getName() {

        long stamp = lock.tryOptimisticRead();
        String value = name;

        if (!lock.validate(stamp)) {

            stamp = lock.readLock();

            try {
                value = name;
            } finally {
                lock.unlockRead(stamp);
            }
        }

        return value;
    }
}
Here, the thread first attempts an optimistic read. If another thread modifies the data during the read, validation fails, and the operation automatically retries using a traditional read lock.

Unlike ReentrantLock, it is not reentrant, meaning a thread cannot safely acquire the same lock multiple times.

In the next chapter, we will explore Atomic Variables and understand how lock-free programming using Compare-And-Swap (CAS) enables high-performance thread-safe operations without traditional locks.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer • Java • Python • Distributed Systems • AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

💬 Comments

Join the Discussion