当前位置:网站首页>Can't be asked again! Reentrantlock source code, drawing a look together!

Can't be asked again! Reentrantlock source code, drawing a look together!

2020-11-06 01:16:00 Liu Zhihang

Preface


After reading JUC Under bag AQS After the source , There are a lot of questions , The biggest question is state What exactly does it mean ? also AQS It mainly defines the in and out of the queue , But access to resources 、 Releasing resources is all handed over to subclass implementations , How is the subclass implemented ? Let's start to understand ReentrantLock.


official account :『 Liu Zhihang 』, Record the skills in work study 、 Development and source notes ; From time to time to share some of the life experience . You are welcome to guide !

Introduce

A mutex and implicit monitor lock that can be re entered synchronized Have the same basic behavior and semantics , But it's more powerful .

Has the following characteristics :

  1. Mutual exclusivity : At the same time, only one thread can get the lock , At this point, other threads request the lock , Will be blocked , And then put it inside the lock for maintenance AQS Blocking the queue .
  2. Reentrancy : maintain state Variable , For the initial 0, When a thread gets a lock ,state Use cas Updated to 1, This thread applies to acquire the lock again , Would be right state Conduct CAS Increasing , The number of repetitions is state, At most 2147483647 . Trying to exceed this limit will be thrown from the lock method Error.
  3. fair / Unfairness : At initialization , It's possible to transmit parameters through constructors , Specifies whether it is a fair lock , Or unfair lock . When set to true when , Lock for fairness , When threads compete for locks , It tends to wait for the longest thread .

Basic use

class X {
    private final ReentrantLock lock = new ReentrantLock();
    // ...

    public void m() {
        lock.lock();  // block until condition holds
        try {
        // ... method body
        } finally {
        lock.unlock()
        }
}
}

Question question ?

First of all, when reading this article , Yes AQS With a certain understanding , If you don't understand , Take a look at the previous article . Graphic explanation AQS

  1. stay AQS Described in the state when , say state The meaning is defined by subclasses , That's in ReentrantLock in state What is the ?
  2. ReentrantLock and AQS What does it matter ?
  3. How the thread gets the lock ?
  4. How the reentrancy of locks is realized ?
  5. Current thread failed to acquire lock , What are the blocked subsequent operations ?
  6. How fair lock and unfair lock are embodied ?
  7. How the lock is released ?

Will be through the source code and drawing the way , Around the above questions , Read and analyze .

Source code analysis

The basic structure

ReentrantLock-uml-DDnlDW

The basic structure is shown in the figure ,ReentrantLock Class implements the interface Lock, At the interface Lock The method of using lock is defined in , The method and meaning are as follows :

public interface Lock {
    
    //  Get the lock , If not , It will block .
    void lock();

    //  Get the lock , If not , It will block . In response to interrupt .
    void lockInterruptibly() throws InterruptedException;

    //  Attempt to acquire lock , If you get , return  true, No access to   return  false
    boolean tryLock();

    //  Attempt to acquire lock , There was no access to , Will wait for a specified time , In response to interrupt .
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    //  Release the lock 
    void unlock();
}

and ReentrantLock It's just realized Lock Interface , And implement these methods , that ReentrantLock and AQS What does it matter ? This needs to see how the internal implementation is achieved .

As can be seen from the class diagram above , stay ReentrantLock There are two inner classes in , Namely NonfairSync FairSync And they both came true abstract class Sync, abstract class Sync Inherited AbstractQueuedSynchronizer namely AQS. The specific code is as follows :

public class ReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

    //  Lock synchronization control basic class .  Subclasses are specific to fair and unfair versions .  Use AQS State to indicate the number of locks held .
    abstract static class Sync extends AbstractQueuedSynchronizer { 
        //  Omit  ...
    }

    static final class NonfairSync extends Sync { 
        //  Unfair lock logic   Omit  ...
    }

    static final class FairSync extends Sync { 
        //  Fair lock logic   Omit  ...
    }
    //  Default unfair lock 
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    //  Specify fair or unfair lock according to the reference ,true  Fair lock ,false  Not fair lock 
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
}

As you can see from the code above :

  1. The basic control of the lock is by NonfairSync and FairSync controlled , And their parents Sync Inherited AQS (AbstractQueuedSynchronizer), That is to say ReentrantLock Implementation and AQS It's about .
  2. NonfairSync Represents unfair lock implementation logic ,FairSync Represents fair lock implementation logic .
  3. It can be seen from the constructor that , On initialization , The default is NonfairSync Not fair lock . You can also specify declared fair or unfair locks , The ginseng true by Fair lock ,false Lock for unfairness .

Specifically ReentrantLock and AQS What is the relationship between , It needs to be analyzed by the locking process .

878c841a671f102ddc2cdeae35faa4cc-pKLxAS

lock

ReentrantLock-lock-4apftk

As shown in the figure , Default declaration unfair lock ,lock Method internal invocation sync.lock(); This should be used in the unfair lock inside lock Lock operation .

final void lock() {
    //  adopt  CAS  Set up  state  value  0 -> 1
    if (compareAndSetState(0, 1))
        //  Setup succeeded. The current thread got the lock 
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //  Setup failed , Call  AQS  Methods , Attempt to acquire lock .
        acquire(1);
}
  1. First of all be Use CAS to update state Value , At this point, you will find that , state The state of the lock represented here . 0 Unlocked ,1 Lock .
  2. Setup failed , Would call AQS Of acquire(1); Method .

Let's take a look at AQS Of acquire Code :

public final void acquire(int arg) {
    // tryAcquire  Try to get  state, If the acquisition fails, it will be added to the queue 
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

Analyze before AQS Source code , It has been introduced tryAcquire It's trying to get state Value ,AQS There are no available methods in , This is implemented by subclasses . So this code is still in NonfairSync Class to implement their own business logic .

static final class NonfairSync extends Sync {
    // NonfairSync  Realization 
    protected final boolean tryAcquire(int acquires) {
        //  Calls a method of the parent class 
        return nonfairTryAcquire(acquires);
    }
}
abstract static class Sync extends AbstractQueuedSynchronizer {
    // NonfairSync  Parent class of  Sync  There is realization in 
    // state  Biography is  1
    final boolean nonfairTryAcquire(int acquires) {
        //  Get the current thread 
        final Thread current = Thread.currentThread();
        //  obtain  state
        int c = getState();
        //  If  c  yes  0 
        if (c == 0) {
            //  Use  cas  Updated to  1
            if (compareAndSetState(0, acquires)) {
                //  Set the holding thread to current 
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (current == getExclusiveOwnerThread()) {
            //  If the current thread holds 
            //  Yes  state  Add up 
            int nextc = c + acquires;
            //  It is not allowed to exceed  int  The maximum of  2147483647 + 1 = -2147483648
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            //  Set up  state  Value 
            setState(nextc);
            return true;
        }
        return false;
    }
}
  1. The current thread is locked , Use it directly CAS Way to state from 0 Updated to 1, The update is successful , Then get the lock , Update failed , Get failed .
  2. When the update fails, call AQS Of acquire(1); Method , The reference here is 1.
  3. tryAcquire Try to acquire the lock again .

    1. state yes 0, Try to get . Return on success true;
    2. state No 0, Determine whether the current thread holds , If it is held by the current thread, it will be used for state Add up .
  4. tryAcquire Lock acquisition failed , Then go AQS Of acquireQueued Logic , Create nodes , And join the waiting queue .

The flow chart is as follows :

  • Starting with a single thread

ReentrantLock-1-1ozuSU

  • At this point, other threads come to request the lock

ReentrantLock-2-zO9REa

  • Lock flow chart

ReentrantLock-nonfair-LcRGc7

How is fair lock embodied

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

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

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            //  Judge whether there is a node queuing 
            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;
    }
}

Pull out the code and compare :

compare-UFOHD0

You can see in the fair lock (FairSync) There is one more judgment condition in

!hasQueuedPredecessors()

hasQueuedPredecessors Method in AQS in , If there is a thread in front of the current thread queuing to return true, If the current thread is empty in the queue's head or queue , return false.

The code is as follows :

public final boolean hasQueuedPredecessors() {

    Node t = tail; 
    Node h = head;
    Node s;

    return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}

If there are nodes queuing up at the time of lock up , Then go to the end of the node , Or else they're going to grab the lock .

Basically, we know the difference between fair lock and unfair lock :

Not fair lock : Whether there are nodes in the queue or not , Will try to get the lock , If the fetch fails , Get into acquire Method , Or try to get it once , And then they're in the queue .

Fair lock : There are already nodes in the queue , Then go to line up behind the node .

tryLock


public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

Directly called Sync Medium nonfairTryAcquire, Attempt to acquire lock , Acquisition failure , Just go back to false, Getting a lock or holding a lock by the current thread is true to state After accumulating, they all return to true.

unlock

public void unlock() {
    sync.release(1);
}

Find out unlock Directly called AQS Of release Method , To release resources .

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

This one is in AQS In the introduction , Also explain tryRelease Implemented by subclass , Now in ReentrantLock Focus on tryRelease The implementation of the .

//  Release resources , The incoming value is  1
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;
}
  1. Get current state Conduct -1 operation ;
  2. Determines whether the current thread is the holding thread ;
  3. If after the release state by 0 , Set the holding thread to null;
  4. Update and return state Value .

summary

Through the above source code and drawing , Basically, there is an answer to the starting question :

Q: stay AQS Described in the state when , say state The meaning is defined by subclasses , That's in ReentrantLock in state What is the ?
A: stay ReentrantLock in state It stands for lock state ,0 No thread gets lock , Greater than or equal to 1 A thread has already acquired a lock , Greater than 1 Indicates that the thread that obtained the lock has re entered multiple times .

Q:ReentrantLock and AQS What does it matter ?
A:ReentrantLock Internally based on AQS Realization , Whether it's a lock state , Or into the waiting line , Lock release is based on AQS Realization .ReentrantLock Fair lock and unfair lock are both NonfairSync、FairSync To achieve , And their father Sync Inherited AQS.

Q: How the thread gets the lock ?
A: Thread by modifying state Field to get the lock .

Q: How the reentrancy of locks is realized ?
A: The current thread found state No 0 , A lock has been obtained , This will determine whether the thread currently getting the lock is itself , If it is , On the other hand state Add up .

Q: Current thread failed to acquire lock , What are the blocked subsequent operations ?
A: Acquisition failure , Will be in AQS Waiting in the queue , Loop through the queue , Monitor whether the previous node is head , If yes , Will try to acquire the lock again .

Q: How fair lock and unfair lock are embodied ?
A: Fair lock is mainly reflected in if there are already queued threads in the current queue , They are directly behind . Unfair locks are no thread queuing regardless of the current queue , Will try to modify it directly state Get the lock .

Q: How the lock is released ?
A: Lock releases resources , the state Conduct -1 operation , If -1 after state by 0, Release the node , Subsequent nodes try to acquire the lock . Here you can see AQS Related logic .

版权声明
本文为[Liu Zhihang]所创,转载请带上原文链接,感谢