Java ReentrantReadWriteLock Example
November 16, 2023
This page will provide Java ReentrantReadWriteLock
example. It is introduced in Java 5. ReentrantReadWriteLock
is the implementation of ReadWriteLock
that implements Lock
interface.
Lock
java.util.concurrent.locks.Lock interface has been introduced in Java 5.Lock
can replace the use of synchronized methods and will facilitate more effective locking system. Lock
is used in multithreaded environment for shared resources. Lock
plays its game in the way that any thread must have to get lock first to access a shared resources protected by Lock
. Only one thread can acquire a lock at a time, once its job is complete, it unlocks the resource for other threads in queue. ReadWriteLock
is an interface which extends Lock
.
ReadWriteLock
java.util.concurrent.locks.ReadWriteLock interface has been introduced in Java 5.ReadWriteLock
is the pair of associated lock for read and write operation. The request for read lock by multiple threads can be acquired simultaneously if there is no write lock request. If a thread acquires write lock on a resource, then no other read or writes lock by any thread can be acquired for that resource. ReadWriteLock
is more effective in the scenario where read operation is more frequent than write operation because read lock can be acquired by multiple threads at the same time for a shared resource.
ReentrantReadWriteLock
java.util.concurrent.locks.ReentrantReadWriteLock class has been introduced in Java 5.ReentrantReadWriteLock
is the implementation of ReadWriteLock
. We will discuss some main properties of ReentrantReadWriteLock
as follows.
Acquisition order
ReentrantReadWriteLock
can behave as fair and non-fair mode both. The default behavior is non-fair. The performance of non-fair lock is better though it is possible that a reader or writer lock can be postponed many times which are continuously trying to lock. In case of fair lock the locking request is fulfilled in the order that either the longest waiting single writer lock or the group of read locks request, whosoever has longest waiting time will acquire lock on the shared resource.
Reentrancy
ReentrantReadWriteLock
provides reentrancy for both read and write lock. It means read and write lock both can be reacquired.
Lock downgrading
The reentrancy in
ReentrantReadWriteLock
can be downgraded from write lock to read lock. It means if a thread has acquired write lock, it can downgrade its lock from write to read lock. The sequence will be that first acquire write lock, do the write operation and then acquire read lock and then unlock the write lock and after read operation finally unlock the read lock.
Find the two main methods of
ReentrantReadWriteLock
.
readLock() : It requests for read operation lock.
writeLock() : It requests for write operation lock.
Example
ForReentrantReadWriteLock
demo, we are creating a ScoreBoard
class which provides two methods, one for update the flag for score update and second for getting score health that can be good or bad. Many threads will call these methods simultaneously and ReentrantReadWriteLock
will handle the concurrency.
ScoreBoard.java
package com.concretepage.concurrent; import java.util.Calendar; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ScoreBoard { private boolean scoreUpdated = false; private int score = 0; String health = "Not Available"; final ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock(); public String getMatchHealth() { rrwl.readLock().lock(); if (scoreUpdated) { rrwl.readLock().unlock(); rrwl.writeLock().lock(); try { if (scoreUpdated) { score = fetchScore(); scoreUpdated = false; } rrwl.readLock().lock(); } finally { rrwl.writeLock().unlock(); } } try { if (score % 2 == 0 ){ health = "Bad Score"; } else { health = "Good Score"; } } finally { rrwl.readLock().unlock(); } return health; } public void updateScore() { try { rrwl.writeLock().lock(); //perform more task here scoreUpdated = true; }finally { rrwl.writeLock().unlock(); } } private int fetchScore() { Calendar calender = Calendar.getInstance(); return calender.get(Calendar.MILLISECOND); } }
package com.concretepage.concurrent; public class ScoreHealthThread implements Runnable { private ScoreBoard scoreBoard; public ScoreHealthThread(ScoreBoard scoreTable) { this.scoreBoard = scoreTable; } @Override public void run() { for(int i= 0; i< 5; i++) { System.out.println("Match Health: "+ scoreBoard.getMatchHealth()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package com.concretepage.concurrent; public class ScoreUpdateThread implements Runnable { private ScoreBoard scoreBoard; public ScoreUpdateThread(ScoreBoard scoreTable) { this.scoreBoard = scoreTable; } @Override public void run() { for(int i= 0; i < 5; i++) { System.out.println("Score Updated."); scoreBoard.updateScore(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package com.concretepage.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { final int threadCount = 2; final ExecutorService exService = Executors.newFixedThreadPool(threadCount); final ScoreBoard scoreBoard = new ScoreBoard(); exService.execute(new ScoreUpdateThread(scoreBoard)); exService.execute(new ScoreHealthThread(scoreBoard)); exService.shutdown(); } }
Score Updated. Match Health: Good Score Score Updated. Match Health: Bad Score Score Updated. Match Health: Good Score Score Updated. Match Health: Bad Score Score Updated. Match Health: Good Score