0
0
JavaConceptBeginner · 3 min read

What is ReentrantLock in Java: Explanation and Example

ReentrantLock in Java is a synchronization tool that allows a thread to lock a resource multiple times without causing a deadlock. It provides more control than the synchronized keyword, such as the ability to try locking without waiting and to interrupt waiting threads.
⚙️

How It Works

Imagine you have a key to a room that you can use to lock the door. If you enter the room and lock the door, you can also lock it again without needing to unlock it first. This is how ReentrantLock works: the same thread can lock the resource multiple times safely.

When a thread locks a ReentrantLock, it gains exclusive access to the resource. If the same thread tries to lock it again, it just increases a count instead of blocking. The thread must unlock the resource the same number of times it locked it to release it fully.

This mechanism helps avoid problems where a thread might accidentally block itself by trying to lock a resource it already holds, which can happen with simpler locks.

💻

Example

This example shows two threads trying to use a ReentrantLock to control access to a shared counter. The lock ensures only one thread changes the counter at a time.

java
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock(); // Acquire the lock
        try {
            counter++;
            System.out.println(Thread.currentThread().getName() + " incremented counter to " + counter);
        } finally {
            lock.unlock(); // Release the lock
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();

        Runnable task = () -> {
            for (int i = 0; i < 3; i++) {
                example.increment();
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");

        t1.start();
        t2.start();
    }
}
Output
Thread-1 incremented counter to 1 Thread-2 incremented counter to 2 Thread-1 incremented counter to 3 Thread-2 incremented counter to 4 Thread-1 incremented counter to 5 Thread-2 incremented counter to 6
🎯

When to Use

Use ReentrantLock when you need more control over locking than what synchronized provides. For example, if you want to try locking without waiting forever, or if you want to interrupt a thread waiting for a lock.

It is useful in complex programs where threads share resources and you want to avoid deadlocks or improve performance by controlling lock fairness.

Real-world cases include managing access to shared data structures, coordinating threads in server applications, or implementing custom synchronization policies.

Key Points

  • ReentrantLock allows the same thread to lock multiple times safely.
  • It offers advanced features like tryLock() and interruptible lock acquisition.
  • It requires explicit unlocking in a finally block to avoid deadlocks.
  • It is more flexible than synchronized but needs careful use.

Key Takeaways

ReentrantLock lets a thread lock a resource multiple times without blocking itself.
It provides advanced locking features like tryLock() and interruptible waits.
Always unlock ReentrantLock in a finally block to prevent deadlocks.
Use it when you need more control than synchronized offers.
It is ideal for complex multi-threaded programs managing shared resources.