Java multi-thread series-"JUC lock" 03 fair lock (1)

Source: Internet
Author: User
Tags lock queue

This chapter introduces the lock Acquisition Mechanism of "fair lock" (the fair lock in this article refers to the fair lock of mutex lock), including: for basic concepts, refer to the Code to obtain the fair lock (based on JDK1.7.0 _ 40) I. tryAcquire () 2. addWaiter () 3. acquireQueued () 4. the lock release mechanism of selfInterrupt () "fair lock" will be introduced in the next chapter, for examples of lock usage, see "Java multithreading series-" JUC lock "02 mutex lock ReentrantLock ". Reprinted please indicate the source: http://www.cnblogs.com/skywang12345/p/3496147.html basic concepts this chapter, we will explain the principle of "thread to obtain the fair lock"; Before the explanation, you need to understand a few basic concepts. The subsequent content is based on these concepts. These concepts may be boring, but from these concepts, we can see some architectures of "java locks, this is helpful for unlocking. 1. AQS -- indicates the AbstractQueuedSynchronizer class. AQS is an abstract class for managing "locks" in java. Many public methods of locks are implemented in this class. AQS is a public parent class of exclusive locks (such as ReentrantLock) and shared locks (such as Semaphore. 2. AQS lock categories: exclusive locks and shared locks. (01) exclusive lock-a lock can only be possessed by one thread lock at a time point. According to the lock acquisition mechanism, it is divided into "fair lock" and "unfair lock ". The fair lock is based on the first-come-first-served rule by the CLH wait thread to obtain the lock fairly. The non-fair lock is used when the thread wants to obtain the lock, it will ignore the CLH waiting queue and directly obtain the lock. A typical example of an exclusive lock is ReentrantLock. In addition, ReentrantReadWriteLock. WriteLock is also an exclusive lock. (02) shared lock-a shared lock that can be owned by multiple threads at the same time. The ReentrantReadWriteLock. ReadLock, javasicbarrier, CountDownLatch, and Semaphore in the juc package are both shared locks. The usage and principles of these locks will be detailed in later sections. 3. The CLH queue-Craig, Landin, and Hagersten lock queue CLH queue is the thread queue in AQS that "waits for locks. In multithreading, in order to protect competing resources from being operated by multiple threads at the same time, we often need to use locks to protect these resources. In an exclusive lock, a competitive resource can only be accessed by one thread lock at a time point, while other threads need to wait. CLH is the queue that manages these "waiting for locks" threads. CLH is a non-blocking FIFO queue. That is to say, when a node is inserted or removed to it, it will not be blocked under the concurrent conditions. Instead, it ensures the atomicity of node insertion and removal through the spin lock and CAS. 4. CAS function -- Compare And Swap CAS function is a comparison And exchange function, which is an atomic operation function. That is, data operated by CAS is performed in an atomic manner. For example, compareAndSetHead (), compareAndSetTail (), compareAndSetNext (), and other functions. These functions are executed in an atomic manner. This chapter focuses on how the "fair lock" acquires the lock. "Fair lock" involves many knowledge points, but in general it is not particularly difficult; if the reader can understand AQS and ReentrantLock. the general meaning of the two classes of java makes it impossible to understand the lock principles and mechanisms. This chapter only gives the author a little understanding of the lock. I hope this part of knowledge can help you understand the process of obtaining the "fair lock" and the framework of the "Lock. The source code of ReentrantLock and AQS in Java1.7.0 _ 40 is provided below for reference only! ReentranLock. java View Code AQS (AbstractQueuedSynchronizer. java) View Code obtains the fair lock (based on JDK1.7.0 _ 40) through the "Example 1" in the previous "Java multithreading series --" JUC lock "02 mutex lock ReentrantLock", we know that, the lock is obtained through the lock () function. Next, we use lock () to expand the process of obtaining the fair lock. 1. lock () in ReentrantLock. in java's FairSync class, its source code is as follows: final void lock () {acquire (1);} note: "The current thread" is actually implemented through acquire (1) obtain the lock. The meaning of "1" is described here. It is a parameter for setting the "Lock status. For an "exclusive lock", when the lock is in the available State, its status value is 0; when the lock is obtained by the thread for the first time, its status value is changed to 1. Because ReentrantLock (fair lock/unfair lock) is reentrant, the "exclusive lock" can be obtained by a single thread multiple times, and the lock state is increased by 1 each time it is acquired. That is to say, when the lock is obtained for the first time, the status value of the lock is set to 1 through acquire (1); when the lock is obtained again, the status value of the lock is set to 2; and so on... this is why the input parameter is 1 when the lock is obtained. Reentrant means that the lock can be obtained multiple times by a single thread. 2. acquire () is implemented in AQS. Its source code is as follows: public final void acquire (int arg) {if (! TryAcquire (arg) & acquireQueued (addWaiter (Node. EXCLUSIVE), arg) selfInterrupt ();} (01) "current thread" first tries to get the lock through tryAcquire. If the result is obtained successfully, the system returns directly. If the attempt fails, the system enters the waiting queue for sorting (there may be threads waiting for the lock ). (02) when the "current thread" fails to be attempted, first use addWaiter (Node. EXCLUSIVE) to add the "current thread" to the end of "CLH Queue (non-blocking FIFO queue. A clh queue is a thread waiting queue. (03) after addWaiter (Node. EXCLUSIVE) is executed, acquireQueued () is called to obtain the lock. Because ReentrantLock is a fair lock at this time, it will obtain the Lock Based on the fairness principle. (04) when "current thread" executes acquireQueued (), it enters the CLH queue for sleep and waits until it gets the lock! If the "current thread" is interrupted during sleep waiting, acquireQueued returns true. At this time, "current thread" calls selfInterrupt () to interrupt itself. As to why we need to interrupt ourselves, I will introduce it later. The above is a general description of acquire. Next, we will divide the function into four parts for gradual parsing. I. tryAcquire () 2. addWaiter () 3. acquireQueued () 4. selfInterrupt () 1. tryAcquire () 1. tryAcquire () fair lock tryAcquire () in ReentrantLock. java implementation in the FairSync class. The source code is as follows: copy the Code protected final boolean tryAcquire (int acquires) {// obtain the "current Thread" final Thread current = Thread. currentThread (); // get the status int c = getState () for the "exclusive lock"; // c = 0 means that "the lock is not owned by any thread lock ", if (c = 0) {// if "the lock is not owned by any thread lock", // determines whether the "current thread" is the first thread in the CLH queue, // If yes, the lock is obtained., Set the lock status, and switch the lock owner to "current thread ". If (! HasQueuedPredecessors () & compareAndSetState (0, acquires) {setExclusiveOwnerThread (current); return true ;}} else if (current = getExclusiveOwnerThread ()) {// if the "exclusive lock" owner is already "current thread", // The lock status will be updated. Int nextc = c + acquires; if (nextc <0) throw new Error ("Maximum lock count exceeded"); setState (nextc); return true;} return false ;} copy the Code Description: according to the code, we can analyze that the role of tryAcquire () is to try to get the lock. Note: here is just a try! If the attempt is successful, true is returned. If the attempt fails, false is returned. Otherwise, the lock is obtained. We will explain how to obtain the lock step by step when the attempt fails. 2. hasQueuedPredecessors () is implemented in AQS. The source code is as follows: copy the public final boolean hasQueuedPredecessors () {Node t = tail; Node h = head; Node s; return h! = T & (s = h. next) = null | s. thread! = Thread. currentThread ();} copy the Code Description: the code can be analyzed separately. hasQueuedPredecessors () checks whether the "current thread" is the first line in the CLH queue, to return whether there is a thread that is longer than the "current thread" in AQS. The following describes head, tail, and Node. 3. Node source code Node is the Node of the CLH queue. Node is implemented in AQS. Its data structure is as follows: copy the code private transient volatile Node head; // the first private transient volatile Node tail in the CLH queue; // end of the clh queue // static final class Node of the CLH queue {static final Node SHARED = new Node (); static final Node EXCLUSIVE = null; // The thread has been canceled, and the corresponding waitStatus value static final int CANCELLED = 1; // "the successor thread of the current thread needs to be unpark (awakened )", the value of waitStatus. // Generally, the successor thread of the current thread is in the blocking state, and the current thread is release or cancel, so it is necessary to wake up the successor thread of the current thread. Static final int SIGNAL =-1; // The thread (in the Condition sleep state) is waiting for the Condition to wake up. The corresponding waitStatus value is static final int CONDITION =-2; // (shared lock) when other threads obtain the "shared lock", the corresponding waitStatus value is static final int PROPAGATE =-3; // when waitStatus is "CANCELLED, SIGNAL, CONDITION, PROPAGATE", it indicates different states respectively, // If waitStatus = 0, it means that the current thread does not belong to any of the above States. Volatile int waitStatus; // volatile Node prev of the previous Node; // volatile Node next of the next Node; // The Thread volatile thread of the Node; // nextWaiter indicates whether the current CLH queue is an exclusive lock queue or a tag of the SHARED lock queue. // If nextWaiter = SHARED, the CLH queue is an exclusive lock queue; // If nextWaiter = EXCLUSIVE (nextWaiter = null), The CLH queue is a "shared lock" queue. Node nextWaiter; // "shared lock" returns true, and "exclusive lock" returns false. Final boolean isShared () {return nextWaiter = SHARED;} // return final Node predecessor () throws NullPointerException {Node p = prev; if (p = null) throw new NullPointerException (); else return p;} Node () {// Used to establish initial head or SHARED marker} // constructor. Thread is the thread corresponding to the node. mode indicates whether the thread lock is "exclusive lock" or "shared lock ". Node (Thread thread, Node mode) {// Used by addWaiter this. nextWaiter = mode; this. thread = thread;} // constructor. Thread is the thread corresponding to the node, and waitStatus is the waiting state of the thread. Node (Thread thread, int waitStatus) {// Used by Condition this. waitStatus = waitStatus; this. thread = thread ;}} copy Code Description: Node is the Node of the CLH queue, representing the "thread queue waiting for Lock ". (01) each Node corresponds to a thread. (02) each Node points to the previous Node and the next Node through prev and next, which respectively represent the previous waiting thread and the next waiting thread. (03) Node saves the waiting state of the thread through waitStatus. (04) Node uses nextWaiter to identify whether a thread is an exclusive lock thread or a shared lock thread. For an EXCLUSIVE lock thread, the value of nextWaiter is EXCLUSIVE. For a SHARED lock thread, the value of nextWaiter is SHARED. 4. compareAndSetState () is implemented in AQS. Its source code is as follows: protected final boolean compareAndSetState (int reverse CT, int update) {return unsafe. compareAndSwapInt (this, stateOffset, exact CT, update);} Description: compareAndSwapInt () is sun. misc. A local method in the Unsafe class. In this regard, we need to know that compareAndSetState (country CT, update) operates the current thread in an atomic way. If the current thread is in the country CT state, it is set to update. 5. setExclusiveOwnerThread () in AbstractOwnableSynchronizer. java implementation, its source code is as follows: // exclusiveOwnerThread is the current Thread with an exclusive lock private transient Thread exclusiveOwnerThread; protected final void handle (Thread t) {exclusiveOwnerThread = t ;} note: setExclusiveOwnerThread () is used to set thread t to a thread with an exclusive lock. 6. getState (), setState () getState () and setState () are both implemented in AQS. The source code is as follows: copy the code // lock status private volatile int state; // set the lock status protected final void setState (int newState) {state = newState;} // obtain the lock status protected final int getState () {return state ;} copy Code Description: state indicates the lock status. For "exclusive locks", state = 0 indicates that the lock is available (that is, the lock is not held by any thread lock ). Because the exclusive lock in java can be reentrant, the value of state can be greater than 1. Conclusion: tryAcquire () is used to let the "current thread" try to obtain the lock. Returns true if the query is successful, and false if the query fails. II. addWaiter (Node. EXCLUSIVE) addWaiter (Node. EXCLUSIVE) is used to create the Node of the "current thread" and record that the lock corresponding to the "current thread" in the Node is of the "EXCLUSIVE lock" type, add the node to the end of the CLH queue. 1. addWaiter () is implemented in AQS. The source code is as follows: copy the code private Node addWaiter (Node mode) {// create a Node, the thread corresponding to the node is "current thread", and the model of the "current thread" lock is mode. Node node = new Node (Thread. currentThread (), mode); Node pred = tail; // if the CLH queue is not empty, add "current thread" to the end of the CLH queue if (pred! = Null) {node. prev = pred; if (compareAndSetTail (pred, node) {pred. next = node; return node ;}// if the CLH queue is empty, call enq () to create a CLH queue, and then add the "current thread" to the CLH queue. Enq (node); return node;} copy Code Description: for "fair lock", addWaiter (Node. EXCLUSIVE) will first create a Node, the Node type is "EXCLUSIVE lock" (Node. EXCLUSIVE) type. Then, add the node to the end of the CLH queue. 2. compareAndSetTail () is implemented in AQS. The source code is as follows: private final boolean compareAndSetTail (Node verification CT, Node update) {return unsafe. compareAndSwapObject (this, tailOffset, exact CT, update);} Note: compareAndSetTail also belongs to CAS functions and is implemented through the "local method. CompareAndSetTail (batch CT, update) performs operations in an atomic manner. It determines whether the tail of the CLH queue is always CT. If yes, it sets the tail of the Team to update. 3. enq () is implemented in AQS. The source code is as follows: copy the private Node enq (final Node node) {for (;) {Node t = tail; if (t = null) {// Must initialize if (compareAndSetHead (new Node () tail = head;} else {node. prev = t; if (compareAndSetTail (t, node) {t. next = node; return t ;}}} copy the Code Description: The Role of enq () is very simple. If the CLH queue is empty, create a new CLH header and add the node to the end of the CLH. Otherwise, add the node directly to the end of CLH. Conclusion: The Role of addWaiter () is to add the current thread to the CLH queue. This means that the current thread is added to the queue of the waiting thread waiting to obtain the lock. 3. Before acquireQueued (), we have added the current thread to the CLH queue. The role of acquireQueued () is to gradually execute the CLH queue thread. If the current thread obtains the lock, it will return; otherwise, the current thread will sleep, it is returned only when the lock is awakened and retrieved again. Next, let's take a look at the specific process of acquireQueued. 1. acquireQueued () is implemented in AQS. The source code is as follows: copy the code final boolean acquireQueued (final Node node, int arg) {boolean failed = true; try {// interrupted indicates whether or not the current thread has been interrupted during CLH queue scheduling. Boolean interrupted = false; for (;) {// obtain the previous node. // Node is the node corresponding to the "current thread", which means "getting the thread waiting for the lock ". Final Node p = node. predecessor (); if (p = head & tryAcquire (arg) {setHead (node); p. next = null; // help GC failed = false; return interrupted;} if (shouldParkAfterFailedAcquire (p, node) & parkAndCheckInterrupt () interrupted = true ;}} finally {if (failed) cancelAcquire (node) ;}} copy the Code Description: acquireQueued () is used to obtain the lock from the queue. 2. commit () shouldParkAfterFailedAcquire () is implemented in AQS. The source code is as follows: copy the code // return "whether the current thread should be blocked" private static boolean shouldParkAfterFailedAcquire (Node pred, Node node) {// The status of the forward node int ws = pred. waitStatus; // If the frontend node is in the SIGNAL state, the current thread needs to be awakened by unpark. In this case, true is returned. If (ws = Node. SIGNAL) return true; // if the forward node is in the "canceled" status, set "current forward node" to "original forward node ". If (ws> 0) {do {node. prev = pred. prev;} while (pred. waitStatus> 0); pred. next = node;} else {// If the frontend node is in the "0" or "shared lock" status, set the frontend node to the SIGNAL status. CompareAndSetWaitStatus (pred, ws, Node. SIGNAL);} return false;} copy the Code Description: (01) for waitStatus, see the following table (the value of waitStatus in the expansion number ),

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.