ReentrantLock
in Java is part of the java.util.concurrent.locks
package and provides a mechanism for thread synchronization similar to synchronized blocks/methods, but with more advanced features and capabilities. A ReentrantLock
is so named because it allows threads to enter the lock on a resource more than once — hence, it’s “reentrant”.
Key Features of ReentrantLock
- Reentrant: A thread can hold the same lock multiple times. The lock keeps a count of the number of holds on it. When the count reaches zero, the lock is released.
- Interruptible Lock Acquisitions: Threads trying to acquire a lock can be interrupted. This is useful for preventing deadlock situations.
- Fairness: Optionally, the lock can be fair, meaning it grants access in a first-come, first-served order, if constructed with the
fair
flag set totrue
. - Lock Polling and Timed Locks: Provides methods to acquire a lock with a timeout (
tryLock()
), allowing threads to wait for a lock for a certain period before giving up. - Condition Support: Supports
Condition
objects, which allow threads to wait for certain conditions to be met while the lock is held, similar to theObject.wait()
andnotify()
methods in synchronized blocks.
Basic Usage Example
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // Acquire the lock
try {
count++;
} finally {
lock.unlock(); // Always ensure to unlock in finally block
}
}
public int getCount() {
return count;
}
public static void main(String[] args) {
Counter counter = new Counter();
// Example usage in threads
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Count: " + counter.getCount());
}
}
In this example, ReentrantLock
is used to ensure that only one thread can execute the increment
method at a time, thus avoiding concurrent modification issues.
When to Use ReentrantLock
- Advanced Synchronization: Use
ReentrantLock
when you need advanced synchronization features that are not available with synchronized blocks/methods. - Fine-Grained Control: It offers more control, such as the ability to try to acquire a lock without waiting forever, and to interrupt a thread waiting to acquire a lock.
- Fairness Policy: When you need a fairness policy for thread scheduling.
Conclusion
ReentrantLock
is a powerful tool for controlling access to shared resources in multi-threaded environments. However, it’s more complex than synchronized blocks and should be used when its advanced features are required. Always remember to unlock in a finally
block to ensure that locks are released even if an exception occurs.