Java display lock re-locking ReentrantLock (seven)

ReentrantLock re-entry lock introduction

re-entry lock ReentrantLock, as the name suggests, is to support the same thread to repeat the lock on the resource. In addition, the lock also supports the fair and unfair choice when acquiring locks.

ReentrantLock, which only supports exclusive get operations, so it only implements the tryAcquire, tryRelease, and isHeldExclusively methods.

ReentrantLock How to implement lock re-entry

Lock re-entry means that any thread can acquire the lock again without being blocked by the lock after acquiring the lock. The implementation of this feature needs to solve two problems:

1, the thread acquires the lock again

needs to identify whether the thread that acquired the lock is the thread that owns the lock in the current period. If it is, then the lock is successfully obtained

2, and the lock is finally released

The lock is obtained by repeating the same thread n times, and after the lock is released for the nth time, other threads can acquire the lock. The final release of the lock requires the lock to count the acquisition. The count indicates the number of times the current lock is repeatedly acquired. When the lock is released, the count is decremented. When the count is equal to 0, the lock has been successfully released.

ReentrantLock is passed. Define the synchronizer to achieve the acquisition and release. The default is to use unfairness. Let's take the unfair access lock as an example to learn how the reentrant lock is implemented:

 final boolean nonfairTryAcquire(int acquires) {
            //current thread 
            final Thread current = Thread.currentThread();
            // Current synchronization status 
            int c = getState();
            // The current synchronization status is equal to 0, indicating that the current thread can obtain the synchronization status 
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                //Set the lock to own the current threadsetExclusiveOwnerThread(current);return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            // The current thread owns the lock and requests it again. The value of the synchronization state is increased by 
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

. It is determined whether the current thread is the thread that acquires the lock to determine whether the operation is successful. If the thread acquires the lock, the synchronization state value is accumulated and returned. True means that the synchronization status is successful.

The thread that successfully acquires the lock acquires the lock again, but only increases the synchronization status value, which also requires the value of the synchronization state to be decremented when the synchronization state is released. As follows, the code to release the lock:

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

If the lock is acquired n times, then the first (n-1) times of the tryRelease( int releases) method must return false, and only the synchronization state is completely released before returning tru. This method will set the synchronization state to 0 as the final release condition. When the synchronization state is 0, the owner thread of the lock is set to null and returns true, indicating successful release.

ReentrantLock How to achieve the fairness and non-fairness of locks

Fairness or not is for the acquisition of locks, if a lock is fair, then the order of the locks should be in the order of service requests. , that is, FIFO.

The nonfairTryAcquire( int acquires) method described above shows that as long as the CAS setting synchronization status is successful, the thread acquires the lock, and the fair lock is different, as shown in the code:

static final class FairSync extends Sync {

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;
        }
}

The difference between this method and nonfairTryAcquire( int acquires) is that there is more hasQueuedPredecessors() method, that is, whether the current node in the synchronization queue has the judgment of the predecessor node. If the method returns true, it indicates that the thread has an earlier request than the current thread. The lock is locked, so you need to wait for the predecessor thread to acquire and release the lock before continuing to acquire the lock.