What is a Java ReadWriteLock?
Locks are an essential feature for multithreaded programming, allowing different threads to operate on the same resources without causing bugs and race conditions. The Java programming language implements several diffferent version of locks, including ReadWriteLocks. But what is a Java ReadWriteLock exactly, and how do Java ReadWriteLocks work?
What are Java ReadWriteLocks?
In computer science, a "lock" controls access to a limited resource in a multithreaded environment. You can think of locks as akin to the physical lock on a door. If multiple people try to pass through a doorway at the same time, they might accidentally crash into each other and cause a injury. Similarly, if two threads try to access the same file or resource at the same time, it could cause crashes or unexpected behavior.
Putting a lock on the door, however, ensures that people can only enter the doorway one at a time, preventing accidents. In the same vein, using locks in computer science ensures that only a limited number of threads can access the same resource simultaneously.
However, notice that errors may only occur if two threads are writing to the same resource, not if they're reading from it. A read-write lock is a type of lock that takes advantage of this fact: multiple threads can read from the same resource at the same time, but only one can be writing. Perhaps the best analogy for a read-write lock is a book on a desk: multiple people can read the text, but only one of them should be writing at one time (or else they might overwrite each other's work).
ReadWriteLock is a Java interface that implements the concept of a read-write lock. By using ReadWriteLocks in Java, developers can theoretically achieve better performance than mutual exclusion locks, which only permit one thread access at a time. In the next section, we'll go into the details of how Java ReadWriteLocks work.
How do Java ReadWriteLocks work?
In Java, ReadWriteLocks are implemented in the java.util.concurrent.locks.ReadWriteLock interface. The ReadWriteLock interface actually holds two locks under the hood. Multiple threads are allowed to hold the reading lock at the same time, but only one can hold the writing lock.
Every class that implements the ReadWriteLock interface must specifically implement the following two Java methods:
- readLock(): Returns the lock used for reading.
- writeLock(): Returns the lock used for writing.
According to the Java documentation, ReadWriteLock implementations are generally straightforward, but need to make decisions about questions such as:
- Are the locks reentrant (i.e. can a thread with the write lock reacquire it, or can it acquire a read lock as well)?
- If a writer is waiting for a read lock and a reader requests one as well, who should get priority? If the reader gets priority, the writer may be waiting forever. If the writer gets priority, this may lower the performance benefits of using a read-write lock in the first place.
In the Java standard library, there is only one class implementing the ReadWriteLock interface: ReentrantReadWriteLock. As the name suggests, this class is a reentrant lock: both readers and writers can reacquire read and write locks. In addition, ReentrantReadWriteLocks allow users to (optionally) use a fairness policy, in which threads are given access to locks in roughly the same order that they requested access.
Java ReadWriteLocks in Redis
Redis is an open-source, in-memory data structure store used to build NoSQL key-value databases, caches, and message brokers. Although it's possible to use distributed locks in Redis by creating your own implementation, it's much more complicated than using the Java standard library. For this reason, many Java developers choose to install a third-party Redis Java client such as Redisson.
The Redisson library offers functionality for many popular Java collections, objects, and constructs in Redis, including Java ReadWriteLocks. Redisson implements read-write locks using the RReadWriteLock interface. Below is a code example of how to use ReadWriteLocks in Redis with Redisson:
RReadWriteLock rwlock = redisson.getReadWriteLock("myLock"); RLock lock = rwlock.readLock(); // or RLock lock = rwlock.writeLock(); // traditional lock method lock.lock(); // or acquire lock and automatically unlock it after 10 seconds lock.lock(10, TimeUnit.SECONDS); // or wait for lock acquisition up to 100 seconds // and automatically unlock it after 10 seconds boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { try { ... } finally { lock.unlock(); } }