In this post we’ll see the usage of java.util.concurrent.locks.ReadWriteLock
interface and its
implementing class ReentrantReadWriteLock in Java with examples.
ReadWriteLock in Java Concurrency
ReadWriteLock as the name itself suggests has a pair of associated locks-
- One for read-only operations
- One for writing operations
The usage of read lock and write lock is as follows-
- The read lock may be held simultaneously by multiple reader threads as long as there are no threads with write lock access.
- The write lock is exclusive. Which means no thread has acquired either read lock or write lock when the thread obtains a write lock.
Benefits of ReadWriteLock
The traditional way of synchronizing the threads requires a mutually exclusive lock. Even if thread is just reading the shared resource (not modifying it) lock is still mutually exclusive i.e. no other thread can enter the critical section while the resource is locked.
A read-write lock allows for a greater level of concurrency in accessing shared data than that permitted by a mutual exclusion lock. It works on a principle that while only a single thread at a time (a writer thread) can modify the shared data, in many cases any number of threads can concurrently read the data (hence reader threads) which may help in increasing performance in a multi-threaded environment.
ReentrantReadWriteLock in Java Concurrency
ReentrantReadWriteLock class in Java is an implementing class of the ReadWriteLock interface. It is used in the following way.
To create a ReentrantReadWriteLock-
ReadWriteLock rwl = new ReentrantReadWriteLock();
For getting read lock-
Lock readLock = rwl.readLock();
For getting write lock-
Lock writeLock = rwl.writeLock();
Here note that ReadLock and WriteLock are the static nested classes with in the ReentrantReadWriteLock class-
- ReentrantReadWriteLock.ReadLock- The lock returned by method ReadWriteLock.readLock().
- ReentrantReadWriteLock.WriteLock- The lock returned by method ReadWriteLock.writeLock().
Locking and unlocking using read lock and write lock is done as follows.
Read lock-rwl.readLock().lock(); try { .. .. }finally{ rwl.readLock().unlock(); }Write Lock-
rwl.writeLock().lock(); try { .. .. }finally{ rwl.writeLock().lock(); }
As you can see ReentrantReadWriteLock follows the same convention as followed with ReentrantLock in Java where call to lock() method is placed before try block and then followed with a try-finally or try-catch-finally block and uses finally block to call unlock() method.
That way unlock() method is called only if the lock is actually acquired and it is also ensured that the unlock() method is called if there is any error after the lock is acquired.
Java ReentrantReadWriteLock constructors
- ReentrantReadWriteLock()- Creates a new ReentrantReadWriteLock with default (nonfair) ordering properties.
- ReentrantReadWriteLock(boolean fair)- Creates a new ReentrantReadWriteLock with the given fairness policy.
ReentrantReadWriteLock example in Java
In the example we’ll have a HashMap that is used by multiple threads. While putting element in the HashMap a write lock is acquired as it is a modifying operation. In case of get method a read lock is used so several threads can get values from the HashMap. Then two write threads and three read threads are started to put and get values from the HashMap.
public class RWLDemo { private final Map<String, String> numberMap = new HashMap<String, String>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); // get method with read lock public String get(String key) { System.out.println("Waiting to acquire lock in get method..."); rwl.readLock().lock(); System.out.println("Acquired read lock in get method"); try { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return numberMap.get(key); } finally { System.out.println("releasing read lock in get method "); rwl.readLock().unlock(); } } // Put method with write lock public String put(String key, String value) { System.out.println("Waiting to acquire lock in put method..."); rwl.writeLock().lock(); System.out.println("Acquired write lock in put method"); try { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return numberMap.put(key, value); } finally { System.out.println("Releasing write lock in put method "); rwl.writeLock().unlock(); } } public static void main(String[] args) { RWLDemo rwlDemo = new RWLDemo(); // To put some initial values in the Map rwlDemo.initialValueInMap(); // Starting Three read threads and two write threads Thread wThread1 = new Thread(new WriterThread(rwlDemo, "3", "Three")); Thread rThread1 = new Thread(new ReadThread(rwlDemo, "1")); Thread rThread2 = new Thread(new ReadThread(rwlDemo, "1")); Thread wThread2 = new Thread(new WriterThread(rwlDemo, "4", "Four")); Thread rThread3 = new Thread(new ReadThread(rwlDemo, "2")); wThread1.start(); rThread1.start(); rThread2.start(); rThread3.start(); wThread2.start(); } private void initialValueInMap(){ // Putting some values in the map numberMap.put("1", "One"); numberMap.put("2", "Two"); } } class ReadThread implements Runnable { RWLDemo rwDemo; String key; ReadThread(RWLDemo rwDemo, String key){ this.rwDemo = rwDemo; this.key = key; } public void run() { System.out.println("Value - " + rwDemo.get(key)); } } class WriterThread implements Runnable { RWLDemo rwDemo; String key; String value; WriterThread(RWLDemo rwDemo, String key, String value){ this.rwDemo = rwDemo; this.key = key; this.value = value; } public void run() { rwDemo.put(key, value); } }Output
Waiting to acquire lock in put method... Waiting to acquire lock in put method... Waiting to acquire lock in get method... Waiting to acquire lock in get method... Acquired read lock in get method Waiting to acquire lock in get method... Acquired read lock in get method releasing read lock in get method Value - Two releasing read lock in get method Acquired write lock in put method Value - One Releasing write lock in put method Acquired read lock in get method releasing read lock in get method Acquired write lock in put method Value - One Releasing write lock in put method
You can see from the output initially two read threads are acquired both can access the locked section. Once read locks are released then only write lock is acquired as write lock has to get exclusive access. There is another read lock that waits for the write lock to release the write lock then only read lock is acquired.
ReentrantReadWriteLock Properties
- There is no reader or writer preference ordering for lock access. However, it does support an optional fairness policy.
- When ReentrantReadWriteLock is constructed as non-fair, which is the default, the order of entry to the read and write lock is unspecified.
- When ReentrantReadWriteLock is constructed as fair, threads contend for entry using an approximately arrival-order policy. When the currently held lock is released, either the longest-waiting single writer thread will be assigned the write lock, or if there is a group of reader threads waiting longer than all waiting writer threads, that group will be assigned the read lock.
- Both read and write lock can reacquire read or write locks in the style of a ReentrantLock. See example here.
- Reentrancy also allows downgrading from the write lock to a read lock, by acquiring the write lock, then the read lock and then releasing the write lock.
- Upgrading from a read lock to the write lock is not possible.
That's all for the topic ReentrantReadWriteLock in Java With Examples. If something is missing or you have something to share about the topic please write a comment.
You may also like
- Synchronized Vs ReentrantLock in Java
- Java Condition Interface
- ConcurrentLinkedQueue in Java With Examples
- CountDownLatch Vs CyclicBarrier in Java
- Java Executor Tutorial - Executor, ExecutorService, ScheduledExecutorService
- ArrayList Internal Implementation in Java
- Generate PDF From XML in Java Using Apache FOP
- Spring Java-based Configuration Example
- Spring Boot Microservices Eureka + Ribbon
- React Portals With Examples
No comments:
Post a Comment