Introduction to ReentrantLock in Concurrent Programming Series

Introduction to ReentrantLock in Concurrent Programming Series

1. What is ReentrantLock?

ReentrantLock is a reentrant Lock implementation that implements the underlying Lock interface. Fair Lock mode and non fair Lock mode are supported.

Typical examples:

ReentrantLocl rlock = new ReentrantLock();
try {
	rlock.lock();
	// Business processing
} finally {
	rlock.unlock();
}

2. What are reentrant locks and non reentrant locks?

  1. Reentrant lock: also known as recursive lock, that is, a thread can repeatedly obtain the lock many times. After a thread obtains the lock, if it still needs to obtain the lock internally, it can obtain the lock directly, provided that it is the same object or class. ReentrantLock and synchronized are both reentrant locks. The most important function of reentrant locks is to avoid deadlock.
  2. Non reentrant lock: it is also called spin lock. The bottom layer is a loop with unsafe and cas mechanisms, which is to cycle until the lock is grabbed. This process is limited by cas. If a thread obtains the lock, cas (compareAndSet) will return 1. Other threads, including themselves, can no longer hold the lock and need to wait for the thread to release the lock.

ReentrantLock is a reentrant lock, so a thread is allowed to acquire a resource lock multiple times. When you call lock for the first time, the count is set to 1. When you get the resource lock again, add 1. Call unlock to unlock. The count is reduced by 1 until it is reduced to 0. Release the lock resource, as shown in the figure:

3. What is the difference between fair lock and unfair lock?

  • Fair lock: multiple threads acquire locks in the order of applying for locks. Threads queue in the queue to acquire locks in order. Only the first thread in the queue can obtain the lock. After obtaining the lock, other threads will block and wait until the thread holding the lock releases the lock, and other threads will be awakened.
  • Unfair lock: multiple threads will compete to obtain the lock. If they cannot obtain the lock, they will enter the queue and wait. If they get the lock through competition, they will directly obtain the lock; Then, after the thread holding the lock releases the lock, all waiting threads will compete for the lock.

synchronized is a non fair lock. ReentrantLock can support non fair locks and fair locks. new ReentrantLock(true) plus true is a fair lock. It is a non fair lock by default

// Set ReentrantLock to fair lock
ReentrantLock lock = new ReentrantLock(true);

4. ReentrantLock method

To view ReentrantLock in the idea editor:

Pick some common methods to describe

  • lock(): if the shared resource is initially idle, calling lock will add 1 to the count and provide the lock to the thread. Will be accumulated
  • unlock(): when the unlock method is called, the count will be reduced by 1. When it is reduced to 0, the resource lock will be released
  • tryLock(): to avoid lock contention, you can use tryLock. If the resource is not held by other threads, it will return true. Locking succeeds. If it has been held by other threads, it will return false. Locking fails
  • tryLock(long timeout, TimeUnit unit): this method attempts to lock, and there is an extra timeout
  • lockInterruptably(): if the resource is idle, this method obtains the lock, allowing the thread to be interrupted by other threads when obtaining the resource. If the thread is in the process of acquiring the lock and other threads rush in, the process of acquiring the lock will be interrupted and return immediately without acquiring the lock
  • getHoldCount(): this method returns a count of the number of locks held on the resource.
  • isHeldByCurrentThread(): this method returns true if the current thread holds a resource lock

5. ReentrantLock example

Example: count big data and start 20 threads. How to ensure thread safety and the correctness of counting?

  • volatile ensures variable visibility
  • CountDownLatch cooperates with multiple threads
  • ReentrantLock ensures thread safety
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockCountExample {

    private static volatile int count = 0;
    static ReentrantLock lock = new ReentrantLock();

    public static void countHandler() {
        lock.lock();
        try {
            count++;
        }finally {
            lock.unlock();
        }
    }

    public static void doCountConcurrent() throws InterruptedException {
        int threads = 20;
        CountDownLatch cdl = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            new Thread(() -> {
                for (int n = 0; n < 10000; n++) {
                    countHandler();
                }
                cdl.countDown();
            }).start();
        }
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        doCountConcurrent();
        System.out.println("Statistical time:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("result:"+ ReentrantLockCountExample.count);
    }

}

Posted on Fri, 03 Dec 2021 05:46:20 -0500 by iloveny