Mastering Java Concurrency: ReentrantLock, Condition, and ReadWriteLock Explained

This article demonstrates how to use Java's ReentrantLock, Condition, and ReentrantReadWriteLock classes to implement synchronization, waiting/notification, producer‑consumer patterns, and read‑write locking, providing code examples, execution results, and tips for avoiding deadlocks and improving thread coordination.

BiCaiJia Technology Team
BiCaiJia Technology Team
BiCaiJia Technology Team
Mastering Java Concurrency: ReentrantLock, Condition, and ReadWriteLock Explained

Using ReentrantLock

Java's ReentrantLock provides the same mutual exclusion as synchronized but with richer features such as lock polling and multiple condition variables.

Example 1: Simple lock usage

public class Service {
    private Lock lock = new ReentrantLock();
    public void testMethod() {
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " i=" + (i + 1));
        }
        lock.unlock();
    }
}

public class MyThread extends Thread {
    private Service service;
    public MyThread(Service service) { this.service = service; }
    @Override
    public void run() { service.testMethod(); }
}

public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        MyThread t1 = new MyThread(service);
        MyThread t2 = new MyThread(service);
        MyThread t3 = new MyThread(service);
        MyThread t4 = new MyThread(service);
        MyThread t5 = new MyThread(service);
        t1.start(); t2.start(); t3.start(); t4.start(); t5.start();
    }
}

Each thread prints its numbers after acquiring the lock; the order of thread groups is nondeterministic because the lock is released only after a thread finishes its loop.

Example 2: Lock with try‑finally and sleep

public class Service {
    private Lock lock = new ReentrantLock();
    public void methodA() {
        try {
            lock.lock();
            System.out.println("methodA begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("methodA end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
        } catch (Exception e) { e.printStackTrace(); }
        finally { lock.unlock(); }
    }
    public void methodB() {
        try {
            lock.lock();
            System.out.println("methodB begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("methodB end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
        } catch (Exception e) { e.printStackTrace(); }
        finally { lock.unlock(); }
    }
}

Running several threads that invoke methodA shows that each thread holds the lock for the whole method, producing sequential output similar to synchronized.

Using Condition for Wait/Notify

The Condition object, created via lock.newCondition(), enables fine‑grained waiting and signalling, allowing multiple independent wait‑sets.

public class Service {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " await time=" + System.currentTimeMillis());
            condition.await();
        } catch (Exception e) { e.printStackTrace(); }
        finally { lock.unlock(); }
    }
    public void signal() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " signal time=" + System.currentTimeMillis());
            condition.signal();
        } catch (Exception e) { e.printStackTrace(); }
        finally { lock.unlock(); }
    }
}

A simple driver starts a waiting thread, sleeps briefly, then calls signal(), producing the expected “await …” followed by “signal …” output.

Multiple Conditions for Selective Notification

public class Service {
    private Lock lock = new ReentrantLock();
    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();
    public void awaitA() { /* lock, await on conditionA, then unlock */ }
    public void awaitB() { /* lock, await on conditionB, then unlock */ }
    public void signalAll_A() { lock.lock(); conditionA.signalAll(); lock.unlock(); }
    public void signalAll_B() { lock.lock(); conditionB.signalAll(); lock.unlock(); }
}

Threads waiting on conditionA are awakened only by signalAll_A(), and similarly for conditionB, demonstrating selective notification.

Producer/Consumer with ReentrantLock and Condition

public class Service {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;
    public void set() {
        lock.lock();
        while (hasValue) condition.await();
        System.out.println("★");
        hasValue = true;
        condition.signal();
        lock.unlock();
    }
    public void get() {
        lock.lock();
        while (!hasValue) condition.await();
        System.out.println("☆");
        hasValue = false;
        condition.signal();
        lock.unlock();
    }
}

Two threads repeatedly call set() and get(), producing an alternating sequence of ★ and ☆. When many threads are started, using signalAll() instead of signal() prevents occasional dead‑lock.

Inspecting Wait Queues

The methods hasQueuedThread(Thread), hasQueuedThreads(), and getWaitQueueLength(Condition) let you query the lock’s internal wait state.

public class MyService {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void waitMethod() { lock.lock(); condition.await(); lock.unlock(); }
    public void notifyMethod() {
        lock.lock();
        System.out.println("Threads waiting: " + lock.getWaitQueueLength(condition));
        condition.signal();
        System.out.println("After signal: " + lock.getWaitQueueLength(condition));
        condition.signalAll();
        System.out.println("After signalAll: " + lock.getWaitQueueLength(condition));
        lock.unlock();
    }
}

Running ten waiting threads and then invoking notifyMethod() shows the queue length decreasing from 10 to 0.

ReentrantReadWriteLock

The read‑write lock allows concurrent reads but exclusive writes.

Read‑share example

public class Service {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + " got read lock " + System.currentTimeMillis());
        Thread.sleep(1000);
        lock.readLock().unlock();
    }
}

Two reader threads acquire the read lock simultaneously, confirming non‑mutual exclusion.

Write‑exclusive example

public class Service {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void write() {
        lock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + " got write lock " + System.currentTimeMillis());
        Thread.sleep(1000);
        lock.writeLock().unlock();
    }
}

Two writer threads serialize their execution, showing exclusive access.

Read‑write mutual exclusion

public class Service {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() { /* lock.readLock() … */ }
    public void write() { /* lock.writeLock() … */ }
}

public class ThreadA extends Thread { public void run() { service.read(); } }
public class ThreadB extends Thread { public void run() { service.write(); } }

When a writer starts first, the subsequent reader waits until the write lock is released, confirming that reads and writes are mutually exclusive.

Overall, the examples illustrate that "read‑read" is concurrent, while "read‑write", "write‑write", and "write‑read" are mutually exclusive.

Reference: http://www.cnblogs.com/umgsai/p/5600103.html

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ReentrantLockConditionReadWriteLock
BiCaiJia Technology Team
Written by

BiCaiJia Technology Team

BiCaiJia Technology Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.