Java > Concurrency and Multithreading > Thread Basics > Thread Synchronization
Using ReentrantLock for Thread Synchronization
This snippet demonstrates thread synchronization using `ReentrantLock` from the `java.util.concurrent.locks` package. `ReentrantLock` provides more control and flexibility compared to synchronized blocks, including fair locking and interruptible waiting.
Code Snippet
This code defines a `ReentrantLockCounter` class with a private `count` variable and a `ReentrantLock` object. The `increment()` and `getCount()` methods use the `lock()` and `unlock()` methods of the `ReentrantLock` to protect the `count` variable from concurrent access. The `try-finally` block ensures that the lock is always released, even if an exception occurs. The `main` method creates two threads that increment the counter, and the final count is printed after both threads have finished executing. `ReentrantLock` ensures only one thread can increment or retrieve the count value at the same time.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockCounter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockCounter counter = new ReentrantLockCounter();
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + counter.getCount());
}
}
Concepts Behind the Snippet
`ReentrantLock` is an implementation of the `Lock` interface that provides reentrant locking semantics. This means that a thread that already holds the lock can acquire it again without blocking. `ReentrantLock` also offers features like fair locking, where threads are granted access to the lock in the order they requested it, and interruptible waiting, where threads can be interrupted while waiting for the lock.
Real-Life Use Case
Consider a resource management system where multiple threads are competing for access to a limited number of resources. `ReentrantLock` can be used to implement a fair locking mechanism, ensuring that threads are granted access to the resources in the order they requested them. This can prevent starvation, where some threads are indefinitely denied access to the resources.
Best Practices
Interview Tip
Be prepared to compare and contrast `ReentrantLock` with the `synchronized` keyword. Explain the advantages of `ReentrantLock`, such as fair locking, interruptible waiting, and the ability to acquire and release locks in different scopes. Also be able to discuss the potential performance implications of using `ReentrantLock`.
When to Use Them
Use `ReentrantLock` when you need more control and flexibility than the `synchronized` keyword provides. This includes scenarios where you need fair locking, interruptible waiting, or the ability to acquire and release locks in different scopes. If the `synchronized` keyword meet the needs, use it.
Memory Footprint
The memory footprint of `ReentrantLock` is similar to that of synchronized blocks. It requires a small amount of memory to store the lock's state (e.g., the thread holding the lock, the number of holds). The main overhead comes from the potential blocking of threads waiting to acquire the lock.
Alternatives
Alternatives to `ReentrantLock` include:
Pros
Cons
FAQ
-
What is fair locking?
Fair locking is a locking mechanism where threads are granted access to the lock in the order they requested it. This prevents starvation, where some threads are indefinitely denied access to the lock. -
What is interruptible waiting?
Interruptible waiting is a locking mechanism where threads can be interrupted while waiting for the lock. This allows threads to respond to external events or cancel their waiting if necessary. -
Why is it important to use a `try-finally` block with `ReentrantLock`?
The `try-finally` block ensures that the `unlock()` method is always called to release the lock, even if an exception occurs. This prevents the lock from being held indefinitely, which could lead to deadlocks and other problems.