Java Multithreading with Reentrant Lock
Table of Contents
- Introduction
- Understanding Reentrant Lock
- Code Example with Explanation
- Advantages of Reentrant Lock
- Conclusion
1. Introduction
In Java multithreading, ensuring thread safety is essential to avoid race conditions when multiple threads access shared resources. Java provides several mechanisms to achieve this, with the synchronized keyword being the most common. However, for more flexibility, the ReentrantLock class from the java.util.concurrent.locks package is used. In this article, we will explore the Reentrant Lock in detail and understand how it works in Java’s multithreading context.
2. Understanding Reentrant Lock
A Reentrant Lock is a type of lock that allows the same thread to enter a locked code block more than once, without causing a deadlock. Unlike synchronized, ReentrantLock provides more control over the locking process with features like:
- Lock fairness (ensuring that threads are granted access based on the order of their requests).
- Ability to interrupt a thread waiting for a lock.
- Option to attempt to acquire the lock without blocking.
Key Features of Reentrant Lock:
Feature | Description |
---|---|
Lock Fairness | Ensures threads access the lock in order of request. |
Interruptible Locks | Allows a thread to be interrupted while waiting for a lock. |
Timeout on Lock | Option to wait for a specified time before giving up on acquiring the lock. |
3. Code Example with Explanation
Below is an example of how to use a Reentrant Lock in Java for thread synchronization. The program demonstrates two threads incrementing a shared counter using a Reentrant Lock to prevent concurrent access issues.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package org.studyeasy; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { public static int counter = 0; // Shared resource static Lock lock = new ReentrantLock(); // Reentrant Lock object public static void main(String[] args) throws InterruptedException { // First thread to increment counter Thread thread1 = new Thread(new Runnable() { @Override public void run() { lock.lock(); // Acquiring lock try { for (int i = 0; i < 100000; i++) { counter++; // Increment counter } } finally { lock.unlock(); // Releasing lock } } }); // Second thread to increment counter Thread thread2 = new Thread(new Runnable() { @Override public void run() { lock.lock(); // Acquiring lock try { for (int i = 0; i < 100000; i++) { counter++; // Increment counter } } finally { lock.unlock(); // Releasing lock } } }); // Start both threads thread1.start(); thread2.start(); // Wait for both threads to complete thread1.join(); thread2.join(); // Output the final value of the counter System.out.println("Final counter value: " + counter); } } |
Code Explanation:
- Shared Resource: The counter variable is shared between two threads. Without synchronization, both threads could access and modify this value simultaneously, leading to incorrect results.
- Reentrant Lock: The ReentrantLock object lock is used to ensure that only one thread accesses the critical section of code (i.e., the increment operation on counter) at a time.
- Lock Acquisition: Each thread acquires the lock using lock.lock() before entering the critical section and releases it using lock.unlock() after exiting the critical section, ensuring mutual exclusion.
- Thread Execution: The join() method ensures that the main thread waits for both threads to complete their execution before printing the final counter value.
Output:
1 |
Final counter value: 200000 |
4. Advantages of Reentrant Lock
Compared to the synchronized keyword, Reentrant Lock offers several advantages:
Feature | synchronized | ReentrantLock |
---|---|---|
Lock Acquisition | Implicitly handles lock control | Manually acquire and release lock |
Lock Fairness | Not supported | Supported |
Timeout Option | Not supported | Supported |
Interruptible Lock | Not supported | Supported |
Flexibility | Less flexible | More flexible (reentrant behavior) |
When to Use Reentrant Lock?
- When you need more control over the locking mechanism.
- When you want to use features like fairness, tryLock, or interruptible locks.
- For advanced thread management scenarios where synchronized isn’t sufficient.
5. Conclusion
The Reentrant Lock in Java provides a powerful way to control thread access to shared resources in a flexible and efficient manner. By offering features like lock fairness, timeout, and interruptibility, it enhances thread synchronization over the traditional synchronized keyword. Understanding when and how to use ReentrantLock is key to managing multithreaded programs efficiently.