Mastering Java ReentrantLock: Blocking, Interruptible, and Timeout Locks Explained

This article explains the Java java.util.concurrent.locks.Lock interface and its ReentrantLock implementation, detailing the advantages of reentrancy, interruptibility, and timeout features, and provides practical code examples for blocking, interruptible, and timed lock acquisition along with best‑practice recommendations.

FunTester
FunTester
FunTester
Mastering Java ReentrantLock: Blocking, Interruptible, and Timeout Locks Explained

Lock Overview

If you have encountered thread‑safety issues, you are likely familiar with the concept of a lock. In Java, the java.util.concurrent.locks.Lock interface defines the contract for lock implementations, and the most commonly used implementation in performance testing is java.util.concurrent.locks.ReentrantLock. Compared with the synchronized keyword, ReentrantLock offers reentrancy, greater flexibility, interruptibility, and higher performance through fair and non‑fair lock modes.

Blocking Lock

The blocking lock is obtained via ReentrantLock#lock(), which has no parameters and blocks the calling thread until the lock is acquired.

package org.funtester.performance.books.chapter02.section3;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Blocking lock example
 */
public class BlockingLockDemo {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(); // create a reentrant lock
        Thread lockTestThread = new Thread(() -> {
            System.out.println(System.currentTimeMillis() + "  Async thread started! " + Thread.currentThread().getName());
            lock.lock(); // acquire lock
            System.out.println(System.currentTimeMillis() + "  Got the lock! " + Thread.currentThread().getName());
            lock.unlock(); // release lock
        });
        lock.lock(); // main thread acquires lock first
        lockTestThread.start(); // start async thread
        Thread.sleep(100); // sleep 100 ms
        System.out.println(System.currentTimeMillis() + "  About to release lock! " + Thread.currentThread().getName());
        lock.unlock(); // release lock
    }
}

The async thread starts, sleeps for 100 ms while the main thread holds the lock, then acquires the lock and prints its log after the main thread releases it. This behavior mirrors that of synchronized in many performance‑testing scenarios.

Interruptible Lock

The interruptible lock is obtained via ReentrantLock#lockInterruptibly(). It blocks like the regular lock, but if the waiting thread is interrupted, it throws java.lang.InterruptedException.

package org.funtester.performance.books.chapter02.section3;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Interruptible lock example
 */
public class InterruptiblyLockDemo {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(); // create a reentrant lock
        Thread lockTestThread = new Thread(() -> {
            try {
                lock.lockInterruptibly(); // acquire lock interruptibly
                System.out.println(System.currentTimeMillis() + "  Got the lock! " + Thread.currentThread().getName());
                lock.unlock(); // release lock
            } catch (InterruptedException e) {
                System.out.println(System.currentTimeMillis() + "  Thread was interrupted! " + Thread.currentThread().getName());
            }
        });
        lock.lock(); // main thread acquires lock first
        lockTestThread.start(); // start async thread
        lockTestThread.interrupt(); // interrupt async thread
        lock.unlock(); // release lock
    }
}

In this example the async thread is interrupted before it can acquire the lock, causing the exception to be caught and a single line of output indicating the interruption.

Timeout Lock

Two timeout‑acquisition methods are available: ReentrantLock#tryLock() (immediate attempt) and ReentrantLock#tryLock(long, TimeUnit) (wait up to the specified timeout). Both return a boolean indicating success.

package org.funtester.performance.books.chapter02.section3;

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

/**
 * Timeout lock example
 */
public class TimeoutLockDemo {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(); // create a reentrant lock
        Thread lockTestThread = new Thread(() -> {
            boolean b = lock.tryLock(); // first immediate attempt
            System.out.println(System.currentTimeMillis() + "  First try result: " + b + " " + Thread.currentThread().getName());
            try {
                boolean b1 = lock.tryLock(3, TimeUnit.SECONDS);
                System.out.println(System.currentTimeMillis() + "  Second try result: " + b1 + " " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                System.out.println(System.currentTimeMillis() + "  Second try interrupted " + Thread.currentThread().getName());
            }
        });
        lock.lock(); // main thread holds lock
        lockTestThread.start(); // start async thread
        Thread.sleep(100); // sleep 100 ms
        lock.unlock(); // release lock
    }
}

The first attempt fails because the main thread holds the lock; the second attempt succeeds after the main thread releases the lock within the 3‑second timeout, demonstrating how timeout locks can control wait periods effectively.

Fair vs Non‑fair Locks

ReentrantLock provides a constructor ReentrantLock(boolean fair). Passing true creates a fair lock where threads acquire the lock in FIFO order; passing false (or using the no‑arg constructor) creates a non‑fair lock that may grant the lock to a thread that arrives later, offering higher throughput.

Best Practices

Always manage lock acquisition and release explicitly; place unlock() in a finally block to guarantee release.

Avoid deadlocks by not mixing different lock types, limiting the number of locks held simultaneously, and using lockInterruptibly() when you need to respond to interruptions.

Prefer the default non‑fair lock for better performance unless strict ordering is required.

Be cautious with reentrancy: unnecessary re‑entry increases lock contention and can introduce deadlock risk if exceptions prevent proper release.

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.

concurrencymultithreadingLockReentrantLock
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

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.