Already profile

ReentrantLock ReentrantLock is a class that implements the Lock interface and is frequently used in practical programming. It supports reentrancy, indicating that it can repeatedly Lock shared resources, that is, the current thread will not be blocked when it obtains the Lock again. ReentrantLock also supports both fair and unfair locking. To fully understand ReentrantLock, it is necessary to learn the synchronization semantics of ReentrantLock:

  • How reentrancy works

  • Fair locks and unfair locks

How reentrancy works

To support reentrancy, two problems need to be solved:

  • 1. When the thread acquires the lock, if the thread that has acquired the lock is the current thread, the thread acquires the lock again


  1. Since the lock is acquired n times, the lock is not fully released until it is released the same n times

NonfairTryAcquire () : nonfairTryAcquire() : nonfairTryAcquire() : nonfairTryAcquire()

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //1. If the lock is not held by any thread, the lock can be acquired by the current threadif (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true; }} //2. If occupied, check whether the occupied thread is the current threadelse if(current == getExclusiveOwnerThread()) { // 3. Int nexTC = c + acquires;if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}Copy the code

In order to support reentrant, the processing logic is added in the second step. If the lock has already been held by a thread, it will continue to check whether the thread holding the lock is the current thread. If so, the synchronization status plus 1 returns true, indicating that the lock can be acquired again. Each recapture increments the synchronization state by one.

For the second problem, still take unfair lock as an example, the core method is tryRelease, the source code is as follows:

protected final boolean tryRelease(int releases) { //1. Synchronization state minus 1 int c = getState() -releases;if(Thread.currentThread() ! = getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free =false;
    if(c == 0) {//2. The lock is released successfully only when the synchronization status is 0true
        free = true;
        setExclusiveOwnerThread(null); } // 3. Lock not fully released, returnfalse
    setState(c);
    return free;
}Copy the code

A reentrant lock cannot be released until the synchronization state is 0, otherwise the lock remains unreleased. If the lock was acquired n times and released n-1 times, false is returned if the lock was not fully released, and true is returned if the lock was released n times.

Fair and unfair locks

ReentrantLock supports two types of locks:

  • Fair lock

  • Not fair lock

Fairness refers to the acquisition of locks. If a lock is fair, the lock acquisition order should conform to the absolute time order on the request, which meets the FIFO. ReentrantLock (); ReentrantLock ();

public ReentrantLock() {
    sync = new NonfairSync();
}Copy the code

ReentrantLock (); Boolean (true = fair lock, false = unfair lock);

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}Copy the code

TryAcquire () : tryAcquire()

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if(! hasQueuedPredecessors() && compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);
            return true; }}else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false; }}Copy the code

The logic is essentially the same as nonfairTryAcquire, the only difference being the addition of the Logical judgment used to determine whether the current node has a precursor node in the synchronous queue known by the name of the hasqueuedEstablished method. If there is a precursor node, the thread requests resources earlier than the current thread. According to fairness, the current thread fails to request resources. If the current node does not have a precursor node, it is necessary to make the following logical judgment. A fair lock obtains the lock from the first node in the synchronization queue every time, whereas a non-fair lock does not necessarily mean that the thread that has just released the lock can acquire the lock again.

Comparison of fair and unfair locks:

  • A fair lock is the first node in the synchronization queue to ensure the absolute sequence of resource requests. However, if a non-fair lock is used, the thread that has just released the lock may continue to acquire the lock next time, and other threads may never obtain the lock, resulting in “hunger”.

  • Fair lock To ensure the absolute sequence in time, frequent context switching is required. Non-fair lock reduces context switching and performance overhead. Therefore, by default, ReentrantLock selects an unfair lock to reduce some context switches and ensure higher throughput.