Java ReentrantLock Example

By Arvind Rai, November 19, 2023
On this page, we will learn to use ReentrantLock in our Java application. It is introduced in Java 5. ReentrantLock handles reentrant mutual exclusion lock in the same way as synchronized method but it differs with synchronized method in many ways. ReentrantLock provides methods to handle the lock pattern. The order of acquiring lock by threads, can be ordered and unordered both by setting fairness as true or false while instantiating ReentrantLock. We will discuss ReentrantLock here in detail.

1. ReentrantLock

java.util.concurrent.locks.ReentrantLock implements java.util.concurrent.locks.Lock. The ReentrantLock behaves same as synchronized method but with more capability. When a thread requests for lock for a shared resource, it is permitted only if resource is unlocked. If thread does not acquire lock, it will be blocked until it acquires lock on the resource.
Why is it named as Reentrant?: ReentrantLock allows the threads to enter into lock more than one times even if the thread is already holding lock. When first time thread enters into lock, a hold count is set to one. Before unlock thread can re-enter into lock again and every time hold count is incremented by one. For every unlock request, hold count is decremented by one and when hold count is 0, it is unlocked.

A thread is holding lock only if the thread has successfully got the lock and yet has not released the lock. ReentrantLock is instantiated either in fairness mode or unfairness mode. The default is unfairness mode. ReentrantLock has two constructor, the default constructor creates unfairness mode and constructor with true argument creates fairness mode object. Fairness mode means ReentrantLock will abide by the order of lock request and in unfairness mode it does not guarantee the order. Though the throughput of unfairness mode is better in comparison to fairness mode. Find some methods of ReentrantLock.
lock()
unlock()
tryLock()
lockInterruptibly()
isHeldByCurrentThread()
getHoldCount()

ReentrantLock is used with try/finally as given below.
public void anyMethod() {
   reentrantLock.lock();
   try {
       //perform task
   }finally {
       reentrantLock.unlock();
   }    
} 
unlock() is called always in finally block to ensure that if there is exception in method body, lock must be released.

2. lock() and unlock() Example

Find the description and usability of lock() and unlock() methods with example.

lock(): When a thread calls lock() method, thread will get lock only if no other thread is already holding the lock on the resource. If no other thread is already holding the lock, the current thread gets the lock and control returns from lock() method immediately. After getting lock, hold count becomes one and if thread again requests for lock, and gets success, hold count will be incremented by one. If the lock is held by another thread, current thread will be blocked until it gets lock successfully. If the hold count for current thread is greater than 0, it means it is holding the lock.

unlock(): If current thread is holding the lock and unlock() is called then the hold count is decremented by 1. When hold count reaches to 0, thread is released and resource is unlocked.

LockUnlockDemo.java
package com.concretepage.concurrent;
import java.util.concurrent.locks.ReentrantLock;

public class LockUnlockDemo implements Task {
	final ReentrantLock reentrantLock = new ReentrantLock();	
	@Override
	public void performTask() {
	    reentrantLock.lock();
	    try { 
	    	 System.out.println(Thread.currentThread().getName() + ": Lock acquired.");
	    	 System.out.println("Processing...");
	    	 Thread.sleep(2000);
	    } catch (InterruptedException e) {
	         e.printStackTrace();
	    } finally {
	    	 System.out.println(Thread.currentThread().getName() + ": Lock released.");
		 reentrantLock.unlock();
            }
	}
} 
Task.java
package com.concretepage.concurrent;
public interface Task {
	public void performTask();
} 
Worker.java
package com.concretepage.concurrent;
public class Worker implements Runnable {
	private Task task;
	public Worker(Task task) {
		this.task = task;
	}
	@Override
	public void run() {
		task.performTask();
	}
} 
Main.java
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 = 5;
		final ExecutorService service = Executors.newFixedThreadPool(threadCount);
		final Task task = new LockUnlockDemo();
		for (int i=0; i< threadCount; i++) {
			service.execute(new Worker(task));
		}
		service.shutdown();
	}
} 
Find the output.
pool-1-thread-4: Lock acquired.
Processing...
pool-1-thread-4: Lock released.
pool-1-thread-2: Lock acquired.
Processing...
pool-1-thread-2: Lock released.
pool-1-thread-3: Lock acquired.
Processing...
pool-1-thread-3: Lock released.
pool-1-thread-1: Lock acquired.
Processing...
pool-1-thread-1: Lock released.
pool-1-thread-5: Lock acquired.
Processing...
pool-1-thread-5: Lock released. 

3. tryLock() Example

Find the description and usability of tryLock() and tryLock(long timeout, TimeUnit unit) with example.

tryLock(): When the thread calls tryLock() on the resource then if the resource is available, thread acquires the lock and tryLock() returns true and hold count is incremented by 1, no matter that other threads are waiting for lock. Even if fairness mode has been set, tryLock() is served first if lock available and otherwise tryLock() returns false and thread does not get blocked.

tryLock(long timeout, TimeUnit unit): We can give a waiting time to tryLock() so that it must wait to acquire lock for the given time if lock is not available. Suppose resource is locked by any thread and current thread calls tryLock(100, TimeUnit.MILLISECONDS) then current thread will wait max for 100 milliseconds to acquire lock and once other thread which already has lock, unlocks it within 100 milliseconds, the current thread will acquire lock and hold count will be incremented by one, even if other threads are waiting for lock. If for the specified time, current thread is not able to acquire lock, this method will return false.

TryLockDemo.java
package com.concretepage.concurrent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockDemo implements Task {
	final ReentrantLock reentrantLock = new ReentrantLock();
	@Override
	public void performTask() {
		try {
			boolean flag = reentrantLock.tryLock(100, TimeUnit.MILLISECONDS);
			if (flag) {
			    try {
					System.out.println(Thread.currentThread().getName() +": Lock acquired.");
					System.out.println("Performing task...");
			    } finally {
					System.out.println(Thread.currentThread().getName() +": Lock released.");
					reentrantLock.unlock();
			    }
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
	}
} 
Change the line in Main class as follows and run it.
final Task task = new TryLockDemo(); 
Find the output.
pool-1-thread-2: Lock acquired.
Performing task...
pool-1-thread-2: Lock released.
pool-1-thread-1: Lock acquired.
Performing task...
pool-1-thread-1: Lock released.
pool-1-thread-3: Lock acquired.
Performing task...
pool-1-thread-3: Lock released.
pool-1-thread-4: Lock acquired.
Performing task...
pool-1-thread-4: Lock released.
pool-1-thread-5: Lock acquired.
Performing task...
pool-1-thread-5: Lock released. 

4. lockInterruptibly() Example

When the current thread calls lockInterruptibly() and the resource is free, this thread acquires lock and the hold count is incremented by one and returns immediately. If the resource is already held by any other thread, then it will wait until it gets lock or any other thread interrupts it. It means if current thread is waiting for lock but mean while any other thread reaches to acquire lock, then the previous one will be interrupted and returns immediately without acquiring lock. Now find the example of lockInterruptibly().

LockInterruptiblyDemo.java
package com.concretepage.concurrent;
import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptiblyDemo implements Task{
	final ReentrantLock reentrantLock = new ReentrantLock();
	@Override
	public void performTask() {
	     try {
		   reentrantLock.lockInterruptibly();
		   //if it is not able to acquire lock because of other threads interrupts,
		   //it will throw InterruptedException and control will go to catch block.
		   try {
				System.out.println(Thread.currentThread().getName() +": Lock acquired.");
				System.out.println("Work on progress...");
			        Thread.sleep(2000);	
		   } finally {
				System.out.println(Thread.currentThread().getName() +": Lock released.");
				reentrantLock.unlock();
		   }
	     } catch (InterruptedException e) {
		   e.printStackTrace();
	     }
	}
} 
Change the line in Main class as follows and run it.
final Task task = new LockInterruptiblyDemo(); 
Find the output.
pool-1-thread-1: Lock acquired.
Work on progress...
pool-1-thread-1: Lock released.
pool-1-thread-2: Lock acquired.
Work on progress...
pool-1-thread-2: Lock released.
pool-1-thread-3: Lock acquired.
Work on progress...
pool-1-thread-3: Lock released.
pool-1-thread-4: Lock acquired.
Work on progress...
pool-1-thread-4: Lock released.
pool-1-thread-5: Lock acquired.
Work on progress...
pool-1-thread-5: Lock released. 

5. isHeldByCurrentThread() and getHoldCount()

Find the description and usability for isHeldByCurrentThread() and getHoldCount() methods.
isHeldByCurrentThread() : This method returns true if lock is held by current thread otherwise false. We usually use this method for debugging and testing purpose.
public void anyMethod() {
   assert !reentrantLock.isHeldByCurrentThread();
   reentrantLock.lock();
   try {
       //perform task
   }finally {
       reentrantLock.unlock();
   }    
} 
The above code snippet ensures that ReentrantLock is being used in non-reentrant way.

getHoldCount(): It returns the number of holds of lock for the given thread. This method is also used for debugging and testing purpose.
public void anyMethod() {
   assert lock.getHoldCount() == 0;
   reentrantLock.lock();
   try {
       //perform task
   }finally {
       reentrantLock.unlock();
   }    
} 
The above code snippet allows the current thread to acquire lock only if it has hold count 0.

6. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us