ReentrantLock

A reentrant lock that allows a thread to repeatedly lock a resource

Class structure

Let’s take a look at the ReentrantLock class structure as shown below:

ReentrantLock implements the Lock interface, and the inner class Sync is a subclass of AQS. Sync has two subclasses, NonfairSync and FairSync, which correspond to unfair and fair locking policies, respectively.

structure

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

ReentrantLock uses unfair policies by default and can specify fair policies when constructed.

Not fair lock

Unfair lock means that the latecomer may overtake the latecomer in the process of competition for the lock

The lock () to obtain the lock
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */
    final void lock(a) {
    	// CAS sets the state value to 1
        if (compareAndSetState(0.1))
        	// If the CAS is successful, the lock has been obtained. In this case, the current thread is set as the owner of the lock object in exclusive mode
            setExclusiveOwnerThread(Thread.currentThread());
        else
        	/ / CAS failure
        	// The same thread may acquire the lock again
        	// It is possible that different threads acquire the lock
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
    	// Call the parent class sync
        returnnonfairTryAcquire(acquires); }}Copy the code
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      // A thread has released the lock
      // It is possible that a blocked thread in the synchronization queue is waking up trying to acquire the lock
        if (compareAndSetState(0, acquires)) {
        	// If the CAS is successful, the lock has been obtained. In this case, the current thread is set as the owner of the lock object in exclusive mode
            setExclusiveOwnerThread(current);
            return true; }}else if (current == getExclusiveOwnerThread()) {
    	// The same thread acquires the lock again
    	/ / state
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
Copy the code

The procedure for obtaining a lock is as follows:

  • Set state to 1 using the CAS
  • If the CAS operation succeeds, the current thread is set as the holder of the exclusive mode lock object
  • If the CAS operation fails, the sync method nonfairTryAcquire is eventually called; The same thread may attempt to acquire the lock again, or another thread may attempt to acquire the lock
  • If the current state is == 0, perform the previous two steps
  • If the current state! = 0, determine whether the current thread is the holder of the lock; If the judgment is true, then state + 1
Unlock () – Releases the lock

An unfair lock is released by calling the tryRelease method of sync, the parent class

protected final boolean tryRelease(int releases) {
    // state subtracts by one
    int c = getState() - releases;
    if(Thread.currentThread() ! = getExclusiveOwnerThread())// Throw an exception if the current thread is not the owner of the current lock
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        // Lock release is completed only when state == 0
        free = true;
        // Empty the lock holder
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
Copy the code

As can be seen from the implementation of lock release, the lock acquisition and lock release operations are equivalent, as shown in the following pseudocode:

ReentrantLock lock = new ReentrantLock();

public void do (a) {
  lock.lock();

  try {

    do(a);// Exit recursion

  } finally{ lock.unlock(); }}Copy the code

Fair lock

A fair lock is one in which locks are acquired in exactly the order at which they were requested, i.e., first come, first served

Lock () – Gets the lock

What’s the difference between a fair lock and an unfair lock in obtaining a lock

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // Different from the unfair lock operation, the fair lock has a judgment condition hasqueued24
        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(a) {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    // h ! = t Indicates that there are waiting nodes in the synchronization queue
    // s = h.ext == null; Head == tail
    // s.thread ! CurrentThread is the first Thread to determine whether the currentThread is the first blocking Thread in the synchronization queue if it is allowed to acquire the lock
    returnh ! = t && ((s = h.next) ==null|| s.thread ! = Thread.currentThread()); }Copy the code
Queries whether any threads have been waiting to acquire longer than the current thread.
Copy the code

The HasqueuedToraise method is used to find out whether other threads have waited longer than the current one. Fair lock is used to ensure the order of acquiring a lock.

Unlock () – Releases the lock

The release of a fair lock is the same as that of an unfair lock

summary

  • How is ReentrantLock reentrant? (By determining whether the current thread is the owner of the current lock object)
  • How to achieve fair locking? (If there are waiting nodes in the current synchronization queue, the lock fails to be obtained.)
  • What is the performance impact of unfair and fair locking? (Fair locks cause a lot of thread switching, while unfair locks cause thread hunger, but less thread switching improves throughput)