This tutorial will help you understand about ReentrantLock class in Java, its method, and implementation using different examples.
ReentrantLock in Java
ReentrantLock class in Java implements the Lock interface. This is part of the java.util.cocurrent
package that implements synchronization along with fairness. This means it provides locks for the threads in the order in which it is present in the waiting list. We can achieve this by passing the fairness argument while creating a ReentrantLock. As the name suggests, a ReentrantLock allows the thread to re-enter the same acquired lock multiple times as long as it holds the lock. Once done, it can release the lock and thereby allowing threads to acquire it.
You might also like Lock Interface in Java
You might be interested in Deadlock in Java
//Empty constructor ReentrantLock rl = new ReentrantLock(); //Constructor with fairness parameter ReentrantLock rl = new ReentrantLock(boolean fairness);
Below is the skeleton code of how to implement a ReentrantLock in Java. It is always a best practice to wrap the unlock()
method within the finally
block. In this way, we can always release the lock even if an unexpected exception arises in the try
block.
ReentrantLock rl = new ReentrantLock(); void method_name() { rl.lock(); try { //shared resource code } catch(Exception e) { //handle exception } finally { rl.unlock(); } }
Methods of ReentrantLock
Below are the methods present in the ReentrantLock class in Java.
Method | Description |
---|---|
int getHoldCount() | Returns the number of holds on the lock by the current thread |
int getQueueLength() | Returns the number of threads waiting to acquire the lock |
int getWaitQueueLength(Condition condition) | Returns the number of threads based on the given condition to acquire the lock |
boolean hasQueuedThread(Thread thread) | Returns true if the given thread is queued waiting for the lock |
boolean hasQueuedThreads() | Returns true if there are threads waiting to acquire the lock |
boolean hasWaiters(Condition condition) | Returns true if there are there are threads waiting to acquire the lock based on the given condition |
boolean isFair() | Returns true if the fair parameter is set true |
boolean isHeldByCurrentThread() | Returns true if the lock is held by current thread |
boolean isLocked() | Returns true if the lock is held by any thread |
void lock() | Acquires the lock |
void lockInterruptibly() | Acquires the lock unless the thread is interrupted |
Condition newCondition() | Returns Condition instance that is used with this lock |
boolean tryLock() | Acquires the lock if it is no held by other threads |
boolean tryLock(long timeperiod, TimeUnit unit) | Acquires the lock if not held by other threads by waiting for the specific time |
void unlock() | Releases the lock |
ReentrantLock Example
The below example illustrates the usage of most of the methods present in the ReentrantLock class. We create 3 different threads to execute the same task.
Inside the run()
method, we first check if the lock is free to acquire using the tryLock()
method. Then we acquire the lock using the lock()
method. We can check if the current thread has acquired the lock using the isLocked()
method. To get the lock hold count, we can use the getHoldCount()
method, and to retrieve the queue length, use the getQueueLength()
method. Finally, we release the lock using the unlock()
method.
import java.util.Date; 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(); Thread t[] = new Thread[3]; for(int i=1;i<3;i++) { t[i] = new Thread(new Task(rl, "Thread " + i)); } for(int i=1;i<3;i++) { t[i].start(); } } } class Task implements Runnable { String name; ReentrantLock rl; public Task(ReentrantLock rl, String name) { this.rl = rl; this.name = name; } public void run() { boolean bFlag = false; while(!bFlag) { boolean bLock = rl.tryLock(); if(bLock) { try { System.out.println(name + " going to acquire the lock at: " + new Date()); Thread.sleep(1000); rl.lock(); try { System.out.println(name + " acquired the lock at: " + new Date()); Thread.sleep(1000); System.out.println("Acquired lock: " + rl.isLocked()); } catch(InterruptedException e) { e.printStackTrace(); } finally { System.out.println(name + " releasing the lock"); rl.unlock(); } System.out.println("Lock hold count: " + rl.getHoldCount()); System.out.println("Queue length: " + rl.getQueueLength()); System.out.println("Acquired lock: " + rl.isLocked()); System.out.println("Completed execution of " + name); bFlag = true; } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(name + " releasing the lock"); rl.unlock(); System.out.println("Lock hold count: " + rl.getHoldCount()); } } else { System.out.println(name + " is waiting for the lock"); try { Thread.sleep(1000); } catch(InterruptedException e) { e.printStackTrace(); } } } } }
Thread 1 is waiting for the lock Thread 2 going to acquire the lock at: Tue Apr 20 15:59:04 IST 2021 Thread 1 is waiting for the lock Thread 2 acquired the lock at: Tue Apr 20 15:59:05 IST 2021 Thread 1 is waiting for the lock Acquired lock: true Thread 2 releasing the lock Lock hold count: 1 Queue length: 0 Acquired lock: true Completed execution of Thread 2 Thread 2 releasing the lock Lock hold count: 0 Thread 1 going to acquire the lock at: Tue Apr 20 15:59:07 IST 2021 Thread 1 acquired the lock at: Tue Apr 20 15:59:08 IST 2021 Acquired lock: true Thread 1 releasing the lock Lock hold count: 1 Queue length: 0 Acquired lock: true Completed execution of Thread 1 Thread 1 releasing the lock Lock hold count: 0
You might be interested in Multithreading Tutorial