Function is introduced

ReentrantLock, a concrete implementation class of a reentrant mutex that has the same basic behavior and semantics as synchronized locks, but with extended capabilities. The ReentrantLock lock is owned by the thread that was last successfully locked and not released. When the lock does not belong to any thread, the thread that called the lock will return and successfully acquire the lock. This method returns immediately if the current thread already owns the lock.

Simply put, the synchronized keyword has the same function as the synchronized keyword and can lock shared resources to ensure access security.

How to use

class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock(); }}Copy the code

Lock. Lock () is used to start a lock, followed by statements that need to be synchronized until lock.unlock() is completed. Unlike synchronized, ReentrantLock requires manual release. Otherwise a deadlock will occur

Source code analysis

Let’s go straight to source analysis from the sample code

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

The first step is to create an instance object of ReentrantLock directly using the no-argument constructor. By default, the no-argument constructor uses an instance object of NonfairSync to assign the member variable sync

Let’s take a closer look at the structure of the ReentrantLock class and the inner class structure of its unfair and fair locks

The internal structure

private final Sync sync;
Copy the code
abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock(); // Abstract lock method, Final Boolean nonfairTryAcquire(int acquires) {final Thread current = thread.currentThread (); Int c = getState(); If (c == 0) {// The lock is not held by any thread, If (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); Return true; } else if (current == getExclusiveOwnerThread()) {int nexTC = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } // Release lock method 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; }}Copy the code

The first is to hold the abstract internal class Sync that inherits from AQS. From the understanding of the previous chapters, we know that AQS mainly does the maintenance related to the synchronization queue that acquires locks

Abstract lock provisioning is defined in the Sync class, which provides methods for obtaining unfair locks and releasing locks

The acquisition code of unfair lock is relatively simple, mainly to determine whether the current lock has been held by other threads, as shown in the following figure

It should be noted that here we can understand the principle of reentrancy:

When a thread acquires the lock, it acquires the lock again if the thread that has already acquired the lock is the current threadCopy the code

If the value is not 0, it indicates that the lock has been reentrant for many times. Only after all the reentrant locks are released can the lock be truly released

Sync’s child implementation classes include fair and unfair locking, and how they implement fairness

Static final class NonfairSync extends Sync {final void lock() {if (compareAndSetState(0, 0); 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }}Copy the code

NonFairSync implements the specific semantics of lock, and calls the acquireQueued method of AQS when preempting the lock. The acquireQueued method of AQS is also used in this chapter

static final class FairSync extends Sync {

    final void lock() {
        acquire(1);
    }

    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
public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; // True return h! = t && ((s = h.next) == null || s.thread ! = Thread.currentThread()); }Copy the code

FairSync mainly implements the semantics of lock, and calls the tryAcquire method in the acquire method. The tryAcquire method is basically the same as the unjust lock acquisition method, the only difference being the HasqueuedToraise method, Determine whether the current node has a front node, which is not the head node. If so, it will enter the queue for queuing; if not, it will acquire the lock

conclusion

Today just shows down already the fairness and fair lock lock the exclusive lock acquisition, after we understand the principle of AQS to see this is very easy, as for the interruptible acquiring a lock and lock timeout gets everyone can own, according to the source code for learning in the understanding of the section in the previous chapter AQS interrupt lock, timeout after acquiring a lock of the source code, I think this section will be very easy to read

This article is written by reading the source code and their own understanding, if there is wrong, please point out, thank you