In highly concurrent systems, threads often need to read shared data frequently but write rarely. Using a traditional synchronized
block or ReentrantLock
can lead to unnecessary contention. Enter the ReadWriteLock — a powerful construct that allows multiple readers or one writer, improving throughput and responsiveness.
This tutorial walks through everything you need to know about ReadWriteLock
, especially ReentrantReadWriteLock
, including real-world examples and performance tips.
🚀 Introduction
🔍 What Is ReadWriteLock?
ReadWriteLock
is an interface in java.util.concurrent.locks
that allows:
- Multiple threads to read the data simultaneously
- Only one thread to write at a time
- No readers during a write operation
Analogy: A library allows many people to read books at once, but only one librarian can rearrange the bookshelves — and must do so without readers in the way.
🧠 When to Use ReadWriteLock
Ideal scenarios:
- Read-heavy workloads (e.g., caches, configuration maps)
- Data structures with many reads but few writes
- Avoiding lock contention in multi-core systems
Avoid for:
- Write-heavy use cases
- Very short-lived reads (lock overhead may dominate)
🔧 Java Syntax and Example
Basic Setup
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SharedData {
private int value = 0;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void write(int newValue) {
lock.writeLock().lock();
try {
value = newValue;
} finally {
lock.writeLock().unlock();
}
}
public int read() {
lock.readLock().lock();
try {
return value;
} finally {
lock.readLock().unlock();
}
}
}
🔄 Thread Lifecycle and Lock Interaction
State | Description |
---|---|
NEW | Thread is created |
RUNNABLE | Competing for read/write lock |
BLOCKED | Waiting for lock |
TERMINATED | Done executing |
📦 Internal Behavior of ReentrantReadWriteLock
- Read and write locks are independent
- Multiple read locks can coexist only if no write lock is held
- Write lock is exclusive and reentrant
- Lock downgrading is possible (write → read)
lock.writeLock().lock();
try {
// write safely
lock.readLock().lock(); // safe to downgrade
} finally {
lock.writeLock().unlock();
lock.readLock().unlock();
}
🔍 ReentrantReadWriteLock Features
- Fairness Policy: Prevent starvation of writers (optional)
- Condition Support: For
await()
/signal()
(viawriteLock().newCondition()
) - TryLock Support: Non-blocking lock attempts
if (lock.readLock().tryLock(100, TimeUnit.MILLISECONDS)) {
try {
// Read safely
} finally {
lock.readLock().unlock();
}
}
🧪 Benchmark Scenario
public class ReadWriteBenchmark {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private int sharedValue = 0;
public void read() {
lock.readLock().lock();
try {
Thread.sleep(10); // simulate read workload
} catch (InterruptedException ignored) {}
finally {
lock.readLock().unlock();
}
}
public void write() {
lock.writeLock().lock();
try {
sharedValue++;
} finally {
lock.writeLock().unlock();
}
}
}
📌 What's New in Java Versions?
Java 8
- Lambdas for
Runnable
, easy lock usage StampedLock
introduced (more scalable than ReadWriteLock)
Java 9
- Flow API (reactive backpressure)
Java 11
- Improved JFR (Java Flight Recorder) for lock monitoring
Java 21
- Structured Concurrency: Manage thread groups
- Virtual Threads: Compatible with lock APIs but with caveats
- Scoped Values: Memory-safe thread-local alternative
🔐 Comparison Table
Feature | ReentrantLock | ReadWriteLock | StampedLock |
---|---|---|---|
Multiple Readers | ❌ | ✅ | ✅ |
One Writer at a Time | ✅ | ✅ | ✅ |
Fairness Option | ✅ | ✅ | ✅ |
Lock Downgrading | ❌ | ✅ | ✅ |
Optimistic Reads | ❌ | ❌ | ✅ |
Complex Logic Overhead | Low | Medium | High |
🧰 Real-World Use Cases
- Cache (e.g., Ehcache, Guava)
- Database connection pool configuration
- Routing table lookups
- Shared game state access
⚠️ Common Pitfalls
- Deadlocks from improper lock ordering
- Starvation of writers with many readers (use fairness)
- Downgrading without correct unlock order
- Using ReadWriteLock where reads are too fast — overhead cancels benefit
✅ Best Practices
- Profile before replacing
synchronized
- Use
try/finally
blocks to avoid leaks - Use
StampedLock
if optimistic reads are beneficial - Keep read/write sections short and atomic
- Enable fairness policy when needed:
new ReentrantReadWriteLock(true)
🧠 Multithreading Design Patterns
- Guarded Blocks — protect data with read/write checks
- Reader-Writer Pattern — natural fit for this lock
- Thread-per-message — protects message queues using read/write segregation
✅ Conclusion and Key Takeaways
ReadWriteLock
is a great tool for read-heavy concurrent systems- It allows multiple readers and ensures safe writes
- Use
ReentrantReadWriteLock
for full control and fairness - Understand trade-offs with
StampedLock
andsynchronized
- Keep critical sections short, profile performance gains
❓ FAQ: ReadWriteLock in Java
1. Is ReadWriteLock
faster than synchronized
?
Yes, for read-heavy workloads. But measure before choosing.
2. Can multiple threads acquire a write lock?
No — only one thread can hold the write lock at a time.
3. What happens if a thread holds both read and write lock?
It can downgrade from write to read, but not the reverse without releasing.
4. Can read locks block write locks forever?
Yes, unless fairness is enabled or writers are prioritized manually.
5. How do I create a fair ReadWriteLock?
Use new ReentrantReadWriteLock(true)
.
6. Can I use ReadWriteLock with tryLock
?
Yes, for non-blocking access attempts.
7. Is ReentrantReadWriteLock reentrant?
Yes — a thread can reacquire the same lock multiple times.
8. Can I use it with Condition
objects?
Only with the write lock, via writeLock().newCondition()
.
9. What’s better: StampedLock or ReadWriteLock?
StampedLock
offers better performance but is harder to use correctly.
10. Are ReadWriteLocks safe with virtual threads?
Yes, but avoid holding locks for long durations in virtual threads.