SoFunction
Updated on 2025-04-14

Java sample code for using ReentrantLock to add unlock

Introduction: Basic concepts and problems of locks

In multithreading programming, in order to ensure that multiple threads do not conflict when accessing shared resources, we usually need to use locks to synchronize access to resources. Java provides different lock mechanisms, among which ReentrantLock is the most commonly used and powerful lock, which belongs to the package and provides more flexible lock control than synchronized.

Although ReentrantLock provides many advantages, improper use of locks can lead to deadlocks, performance degradation, or unmaintainable code. This article will explore in-depth how to use ReentrantLock gracefully, avoid common pitfalls, and improve code maintainability.

1. The basic concept of ReentrantLock

Before discussing how to use ReentrantLock elegantly, let’s quickly review its basic concepts.

ReentrantLock is an explicit lock provided by Java, which provides greater flexibility than synchronized. ReentrantLock offers the following advantages over synchronized locks:

  • Reentrability: A thread can obtain the same lock multiple times without being blocked.
  • Interruptible lock request:uselockInterruptiblyMethods can make the thread respond to interrupts while waiting for the lock.
  • Fairness: You can choose fair lock (FIFO queue) or non-fair lock to avoid thread hunger.
  • Manual unlock:passunlockMethods to release the lock can accurately control the release timing of the lock.

2. Basic mode of using ReentrantLock

Let's see how to use itReentrantLockLock and unlock.

import ;

public class ReentrantLockExample {
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Runnable task = () -> {
            ();  // Get the lock            try {
                // Execute critical area code                (().getName() + " is processing the task.");
            } finally {
                ();  // Make sure to unlock            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        ();
        ();
    }
}

How to understand:

  • ()Will try to acquire the lock. If the lock is already held by another thread, the current thread will be blocked.
  • unlock()Used to release the lock, it must be placedfinallyIn the block, ensure that the release of the lock can be executed even in the event of an exception.

3. How to handle the locking and unlocking of ReentrantLock gracefully?

AlthoughReentrantLockProvides flexibility, but incorrect usage methods can lead to problems such as deadlocks and resource leakage. To avoid these problems, we can follow the following best practices:

1. Use finally block to ensure unlocking

The most common mistake is that forgetting to release the lock causes a deadlock, or an exception is thrown when the lock is released. In order to ensure the release of the lock, even if an exception occurs, it should always befinallyUnlock in the block.

();
try {
    // Execute critical area code} finally {
    ();  // Ensure the lock is released}

2. Use lockInterruptibly to implement interrupt lock request

In some cases, threads may be suspended for a long time when acquiring the lock and cannot respond to interrupts in time. By usinglockInterruptibly, We can ensure that the thread responds to interrupts while waiting for the lock.

public void safeMethod() {
    try {
        ();  // Can respond to interrupts        // Execute critical area code    } catch (InterruptedException e) {
        ().interrupt();  // Processing interrupt        ("Thread was interrupted while waiting for the lock.");
    } finally {
        ();
    }
}

3. Use tryLock to avoid blocking

ReentrantLockAlso providedtryLockMethod, it tries to acquire the lock and returns immediately. If the lock cannot be acquired, the thread will not be blocked, but will returnfalse, let us take other measures (such as retrying or skipping operations).

if (()) {
    try {
        // Execute critical area code    } finally {
        ();
    }
} else {
    // Lock acquisition failed, you can choose to try again or perform other operations    ("Could not acquire lock. Try again later.");
}

4. Use fair locks to avoid thread hunger

By default,ReentrantLockIt is an unfair lock, which means that there is no strict order in which the thread acquires the locks. If you want the thread to acquire the lock in the order in which the lock is requested (avoid thread hunger), you can create a fair lock.

ReentrantLock fairLock = new ReentrantLock(true);  // Fair lock

Using fair locks may result in a slight decline in performance because threads need to acquire locks in queue order, but it can avoid situations where some threads cannot acquire locks for a long time.

4. Tips to avoid deadlocks

DeadlockIt is one of the most common problems in multithreaded programming, which occurs when two or more threads cannot continue execution because they are waiting for each other to release the lock. To avoid deadlocks, we can follow the following points:

1. The order of acquisition of locks

Make sure all threads acquire locks in the same order. For example, if thread A needs to acquire lock X and lock Y, thread B should also acquire lock X and lock Y in the same order to avoid waiting for each other.

2. Use tryLock to avoid infinite waiting

When a thread cannot acquire the lock, using the tryLock method can prevent the thread from falling into an infinite waiting state and set a timeout time for the thread.

if (() && ()) {
    try {
        // Execute critical area code    } finally {
        ();
        ();
    }
} else {
    // Lock acquisition failed, other logic is executed    ("Could not acquire both locks, retrying...");
}

By setting the timeout time, if the two locks cannot be acquired within the specified time, the thread will give up waiting to avoid deadlocks.

5. Summary: Best practices for elegant use of ReentrantLock

ReentrantLockIt is a very powerful tool that can provide us with comparisonsynchronizedMore fine-grained lock control. However, to use it gracefully, there are several best practices to follow:

  1. Ensure the lock is released: AlwaysunlockPut infinallyIn the block, make sure that the lock can be released even if an exception occurs.
  2. uselockInterruptibly: Used in scenarios that may be blocked for a long timelockInterruptiblyto respond to interrupts.
  3. usetryLock: Avoid endless blocking of threads due to inability to acquire locks, throughtryLockTo detect the lock status and make corresponding processing.
  4. Use fair lock: Use fair locks when it is necessary to ensure the fairness of the lock to avoid thread hunger.
  5. Avoid deadlocks: Obtain the order of obtaining the lock and use it reasonablytryLockTo avoid deadlocks.

By following these principles, we can useReentrantLockAvoid common pitfalls, improve the stability and maintainability of the code, and write more elegant multi-threaded code.

The above is the detailed content of the sample code for Java using ReentrantLock to add unlock. For more information about Java ReentrantLock to add unlock, please follow my other related articles!