Chapter 13: Explicit Locks
In earlier chapters, we explored the use of the synchronized
keyword as a means to ensure thread safety. While effective and easy to use, synchronized
offers limited flexibility when advanced concurrency control is needed. Chapter 13 of Java Concurrency in Practice introduces us to the Lock and ReadWriteLock interfaces, which give developers fine-grained control over how threads access shared resources.
Lock Interface
The java.util.concurrent.locks.Lock
interface allows for explicit lock management. Its main implementation, ReentrantLock
, offers powerful features that go beyond intrinsic locks:
lock()
— Acquires the lock unconditionally.tryLock()
— Tries to acquire the lock without blocking.tryLock(timeout, unit)
— Tries to acquire the lock within a given time.lockInterruptibly()
— Allows the thread to be interrupted while waiting for the lock.unlock()
— Releases the lock.
Comparison: synchronized vs ReentrantLock
Feature | synchronized | ReentrantLock |
---|---|---|
Interruptible | No | Yes |
Timeout | No | Yes |
Try-acquire | No | Yes |
Multiple condition queues | No | Yes |
Fairness policy | No | Yes (optional) |
Ease of use | High | Moderate (needs try-finally) |
The program DeadlockAvoidance.java demonstrates how to use tryLock()
with randomized retries and backoff to avoid deadlock in a high-concurrency transfer system. Unlike synchronized blocks, this approach makes failure to acquire a lock recoverable.
In TimedLocking.java, the program uses tryLock(timeout, unit)
to ensure that operations on a shared resource complete within a time budget. If the lock cannot be acquired in time, the operation fails gracefully.
InterruptibleLocking.java showcases lockInterruptibly()
, which allows threads to respond to interrupts while waiting for a lock. This enables responsive cancellation, making the system more robust under dynamic load or timeout conditions.
ReadWriteLock Interface
For applications with heavy read activity and infrequent writes, the ReadWriteLock
interface offers an efficient solution. Its main implementation, ReentrantReadWriteLock
, provides two locks:
readLock()
— Allows multiple readers if no writer holds the lock.writeLock()
— Exclusive lock for writing.
The ReadWriteMap.java example demonstrates wrapping a Map
with a read-write lock to allow concurrent access by multiple reader threads while ensuring safe, exclusive access for writers.
Relevance in the Loom Era
Even with the introduction of virtual threads in Java via Project Loom (Java 21+), the Lock
and ReadWriteLock
interfaces remain highly relevant. While virtual threads encourage the return to simpler synchronization models (like synchronized
), these explicit lock APIs are still crucial for scenarios that demand advanced control:
- Timeout-sensitive operations
- Interruptible lock acquisition
- Fairness and ordering guarantees
- Complex coordination using
Condition
variables
Moreover, ReentrantLock
and ReentrantReadWriteLock
can be used effectively with virtual threads as long as the developer avoids constructs that pin virtual threads to platform threads (such as blocking I/O or holding locks for long durations).
Conclusion
Chapter 13 opens the door to powerful locking patterns that go far beyond the capabilities of synchronized
. Whether you're building systems with traditional threads or embracing the new virtual thread model in Loom, explicit locks give you the flexibility to build responsive and robust concurrent applications.
No comments:
Post a Comment