April 8, 2022

ReentrantLock in Java With Examples

ReentrantLock in Java is part of java.util.concurrent.locks package with in the Java concurrency API added in Java 5 which also has other classes like ConcurrentHashMap, CountDownLatch. ReentrantLock class in Java implements java.util.concurrent.locks.Lock interface and provides many locking operations.

ReentrantLock in Java Concurrency

The traditional way of acquiring a mutual exclusion lock in Java is to use synchronized keyword but ReentrantLock provides more flexibility. ReentrantLock allows a lock to be acquired and released in different scopes and allows multiple locks to be acquired and released in any order.

With implementation of the Lock interfaces like ReentrantLock acquiring and releasing of lock is more explicit. It has methods like lock() to acquire the lock and unlock() to release the lock.

ReentrantLock in Java provides many other features like fairness policy, ability to interrupt, trying to acquire a lock if it is not held by another thread and thread waiting for a lock only for a specified period.

ReentrantLock in Java is used in the following way-

public void myMethod(){       
  rentrantlock.lock();
  try {
    ..
    ..
  } catch(Exception exp){
    exp.printStackTrace();
  }finally{
    rentrantlock.unlock();
  }
} 

The conventional way to acquire a lock using ReentrantLock is to call lock() method before try block and then follow it with a try-catch-finally block (or try-finally block) and use finally block to call unlock() method.

You should not include call to lock() method with in the try block because some exception may get thrown before the call to lock() method or while acquiring lock which may result in lock not getting acquired. But finally block having call to unlock() will still be called resulting in IllegalMonitorStateException.

You do need to unlock an acquired lock even if something goes wrong after acquiring the lock that is why call to lock() should immediately be followed by try block along with finally.

Why call it a ReentrantLock

ReentractLock is named so because the thread currently holding the lock can reenter the lock. There is an acquisition count associated with the lock, whenever lock is acquired the acquisition count is incremented by 1.

With every call to unlock() method the acquisition count is decremented by one and the resource is unlocked when the count reaches zero.

Java ReentrantLock class constructor

  • ReentrantLock()- Creates an instance of ReentrantLock.
  • ReentrantLock(boolean fair)- Creates an instance of ReentrantLock with the given fairness policy.

ReentrantLock Java example

In the example four runnable tasks are executed. Same lock is entered again in the method lockMethod(). You can see that the unlock() method is called twice to release the lock.

Also tryLock() method is used to acquire the lock, in the example if the thread is not able to acquire the lock it doesn’t wait, if you want you can add a loop to make that thread keep on executing until it acquires a lock.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
  public static void main(String[] args) {
    ReentrantLock rl = new ReentrantLock();
    ExecutorService ex = Executors.newFixedThreadPool(2);
    for(int i = 0; i < 4; i++){
      ex.execute(new RClass("Thread-"+i, rl));
    }
    ex.shutdown();
  }
}

class RClass implements Runnable {
  private String threadName;
  ReentrantLock rl;
  RClass(String threadName, ReentrantLock rl){
    this.threadName = threadName;
    this.rl = rl;
  }
  @Override
  public void run() {
    System.out.println("In run method trying to acquire lock- thread " + threadName);
    //acquiring lock
    boolean flag = rl.tryLock();
    if(flag){
      try {
        System.out.println("Thread " + threadName + " has got lock");
        lockMethod();
      } finally{  	
        rl.unlock();
        System.out.println("Count of locks held by thread " + threadName + 
             " - " + rl.getHoldCount());
      } 
    }else{
       System.out.println("Thread- " + threadName + " not able to acquire lock.");
    }
  }

  public void lockMethod(){
    System.out.println("In lockMethod, thread " + threadName + 
      " is waiting to get lock");            
    rl.lock();
    try {        	
      System.out.println("Count of locks held by thread " + threadName + 
          " - " + rl.getHoldCount());
    } finally{
      rl.unlock();
    }
  }    
} 
Output
In run method trying to acquire lock- thread Thread-0
In run method trying to acquire lock- thread Thread-1
Thread Thread-0 has got lock
Thread- Thread-1 not able to acquire lock.
In lockMethod, thread Thread-0 is waiting to get lock
In run method trying to acquire lock- thread Thread-2
Count of locks held by thread Thread-0 - 2
Thread- Thread-2 not able to acquire lock.
Count of locks held by thread Thread-0 - 0
In run method trying to acquire lock- thread Thread-3
Thread Thread-3 has got lock
In lockMethod, thread Thread-3 is waiting to get lock
Count of locks held by thread Thread-3 - 2
Count of locks held by thread Thread-3 - 0

ReentrantLock class methods

Some of the important methods of the ReentrantLock class in Java are as follows-
  • getHoldCount()- Queries the number of holds on this lock by the current thread.
  • isFair()- Returns true if this lock has fairness set true.
  • isLocked()- Queries if this lock is held by any thread.
  • lock()- Acquires the lock.
  • lockInterruptibly()- Acquires the lock unless the current thread is interrupted.
  • tryLock()- Acquires the lock only if it is not held by another thread at the time of invocation.
  • tryLock(long timeout, TimeUnit unit)- Acquires the lock if it is not held by another thread within the given waiting time and the current thread has not been interrupted.
  • unlock()- Attempts to release this lock.

Drawbacks of the ReentrantLock in Java

  1. If not used in the proper way where lock() method is not called immediately before the try block and not unlocked in the finally block then the lock may not get unlocked in case any exception is thrown.
  2. unlock() method has to be called explicitly as many times as the lock() method is called on a thread otherwise lock will not be released causing performance problems.
  3. Programs using fair locks accessed by many threads may display lower overall throughput.

That's all for the topic ReentrantLock 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

No comments:

Post a Comment