Refer to the link: www.bilibili.com/video/BV1ta…

AQS knowledge is required

Reentrant means that it is thread-safe to re-enter the same subroutine while executing by a single thread.

If it is non-reentrant, A deadlock will occur when A tries to request the lock again if A acquires the lock

In simple terms, a thread can repeatedly acquire the lock n times without releasing it, and release the lock n times.

Let’s talk about RenentrantLocak, an implementation of the reentrant lock.

First, let’s look at the inheritance of RenentrantLocak, which implements the Lock interface, following the abstract definition of the Lock interface.

public class ReentrantLock implements Lock.java.io.Serializable
Copy the code

Lock

Lock provides an alternative to synchronized.

Operations are broader, more flexible structures are supported, and multiple Condition objects can be associated.

/ / acquiring a lock
void lock(a);

// Get the lock. If interrupted while waiting for the lock, exit the wait and throw an exception
void lockInterruptibly(a) throws InterruptedException;

// Try to get the lock and return immediately
boolean tryLock(a);

// Attempts to acquire the lock for a period of time, and if the period is interrupted, an interrupt exception is thrown
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

/ / releases the lock
void unlock(a);

// Create a Condition object bound to the current Lock
Condition newCondition(a);
Copy the code

Fair and unfair locks

Fair lock: The lock is allocated according to the order of request, such as the FIFO mechanism in AQS to achieve fair lock

Unfair locks: Locks are not allocated in the order in which they are requested, but starvation can occur in this manner

Why do you design unfair locks?

In fact, unfair locking tends to be more efficient and may improve concurrency performance.

When a thread is awakened, there is a short delay in the thread state switch. The unfair locking mechanism allows the thread to preempt the lock during this period of time, and quickly process the content.

This is one of the reasons that unfair locks perform better than fair locks.

A constructor

Constructively, ReentrantLock is actually the Sync class, and you can choose between FairSync and NonfairSync (the default) with parameters.

public ReentrantLock(a) {
    sync = new NonfairSync();
}

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

attribute

final Sync sync

Sync is the ReentrantLock internal abstract class that inherits AQS.

NonfairSync and FairSync are Sync implementation subclasses that implement unfair and fair locks, respectively.

abstract static class Sync extends AbstractQueuedSynchronizer  {... }static final class NonfairSync extends Sync {... }static final class FairSync extends Sync {... }Copy the code

If nonfairTryAcquire(int acquires) is implemented in Sync, the name of the nonfairTryAcquire(int acquires) method is defined in its parent class.

Here’s a look at the method:

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
    
    		// 0 indicates that the lock is idle. You can perform CAS to obtain the lock
    		// If the state change succeeds, the lock is obtained
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true; }}// If the lock is occupied, check whether the current thread is the thread that holds the lock
            else if (current == getExclusiveOwnerThread()) {
                // reenter, then count +1
                int nextc = c + acquires;
                if (nextc < 0) // overflow, that is, prevent the number of threads from overrunning and turning negative
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
    		// No lock was obtained
            return false;
}
Copy the code

NonfairSync

NonfairSync implements unfair locking by providing double preemption:

  • Preemption during lock
  • Override of the tryAcquire call to nonfairTryAcquire
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
	
    final void lock(a) {
        // Attempt to acquire the lock [first preemption]
        if (compareAndSetState(0.1))
            // On success, the lock is acquired
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // Call AQS acquire method on failure
            // tryAcquire was overridden because it was called in acquire
            // Hence the trayAcquire method below [second preemption]
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        returnnonfairTryAcquire(acquires); }}Copy the code

FairSync

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock(a) {
    	// Join the queue directly
        acquire(1);
    }
    
    / / rewrite tryAcquire
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        
        // Check whether the lock is free
        if (c == 0) {
            // Check whether there is a waiting node ahead
            if(! hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true; }}// Reentrant mechanism
        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

methods

lock

It is implemented in sync and can be called directly.

public void lock(a) {
    sync.lock();
}
Copy the code

trylock

public boolean tryLock(a) {
    return sync.nonfairTryAcquire(1);
}
Copy the code

unlock

public void unlock(a) {
    sync.release(1);
}
Copy the code