Java concurrent series of AbstractQueuedSynchronizer source code analysis (summary analysis ),

Source: Internet
Author: User

Java concurrent series of AbstractQueuedSynchronizer source code analysis (summary analysis ),

To learn Java concurrent programming, you have to understand the java. util. concurrent package. This package contains many frequently used concurrent tool classes, such as ReentrantLock, CountDownLatch, CyclicBarrier, and Semaphore. The underlying implementation of these classes depends on the AbstractQueuedSynchronizer class. This shows the importance of this class. So in the Java concurrent series of articles, I first analyze the class AbstractQueuedSynchronizer. Because this class is important and the code is long, in order to make the analysis as thorough as possible, I decided to use four articles to give a complete introduction to this class. As an overview, this article mainly gives readers a preliminary understanding of this class. To make the description simple, AQS will be used to represent this class in some areas in the future.

1. What does the AbstractQueuedSynchronizer class do?

I believe many readers have used ReentrantLock, but they do not know the existence of AbstractQueuedSynchronizer. In fact, ReentrantLock implements an internal class Sync, which inherits the AbstractQueuedSynchronizer. The implementation of all lock mechanisms depends on the internal classes of Sync. It can also be said that the implementation of ReentrantLock depends on the AbstractQueuedSynchronizer class. Similarly, CountDownLatch, CyclicBarrier, and Semaphore classes use the same method to implement lock control. It can be seen that AbstractQueuedSynchronizer is the cornerstone of these classes. So what does AQS implement internally so that these classes depend on it? In this case, AQS provides infrastructure for these classes, that is, a password lock. These classes can set their own passwords after they have a password lock. In addition, AQS also provides a queuing area and thread training. We know that a thread is like a primitive barbarian. It is not polite, and it only performs a direct collision, so you have to teach it step by step, tell it when to queue up, where to queue, what to do before queuing, and what to do after queuing. All these education work has been completed by AQS. the threads learned from AQS have become very civilized and polite, and they are no longer primitive barbarian, so in the future, we only need to deal with these civilized threads. Never have too much contact with the original threads!

2. Why does AbstractQueuedSynchronizer provide a password lock?

// The head Node of the synchronization queue is private transient volatile Node head; // The End Node of the synchronization queue is private transient volatile Node tail; // The synchronization status is private volatile int state; // obtain the synchronization state protected final int getState () {return state;} // set the synchronization state protected final void setState (int newState) {state = newState ;} // set the synchronization state in CAS mode protected final boolean compareAndSetState (int exact CT, int update) {return unsafe. compareAndSwapInt (this, stateOffset, exact CT, update );}

The code above lists all the member variables of AQS. We can see that there are only three member variables of AQS, namely, synchronization queue header node reference, synchronization queue End Node reference, and synchronization status. Note that the three member variables are modified using the volatile keyword, which ensures that the modifications made by multiple threads are visible to the memory. The core of the entire class is the synchronization status. We can see that the synchronization status is actually an int type variable. You can regard this synchronization status as a password lock, the password lock is also locked from the room. The specific value of the state is equivalent to the password controlling the opening and closing of the password lock. Of course, the password of this lock is defined by the sub-classes. For example, in ReentrantLock, if the value of state is 0, the lock is opened. If the value of state is greater than 0, the lock is locked, in Semaphore, if the state is greater than 0, the lock is opened. If the state is equal to 0, the lock is locked.

3. How is the queuing area of AbstractQueuedSynchronizer implemented?

AbstractQueuedSynchronizer has two internal Queues: Synchronous queue and conditional queue. We can see that there is only one synchronization queue, and multiple conditional queues can. The nodes in the synchronization queue hold the reference of the front and back nodes respectively, while the node in the condition queue has only one reference pointing to the next node. In the figure, T indicates a thread. Each node contains a thread. After the thread fails to obtain the lock, it first enters the synchronization queue. To enter the condition queue, the thread must hold the lock. Next we will look at the structure of each node in the queue.

// Synchronization queue Node static final class Node {static final Node SHARED = new Node (); // indicates that the current thread holds the lock static final Node EXCLUSIVE = null in SHARED mode; // indicates that the current thread holds the lock static final int CANCELLED = 1 in exclusive mode; // indicates that the current node has canceled the acquisition of the lock static final int SIGNAL =-1; // indicates that the thread of the successor node needs to run static final int CONDITION =-2; // indicates that the current node queues static final int PROPAGATE =-3 in the CONDITION queue; // It indicates that the successor Node can directly obtain the lock volatile int waitStatus; // It indicates the waiting status of the current Node volatile Node prev; // It indicates the frontend Node volatile Node next in the synchronization queue; // It indicates the subsequent Node volatile Thread thread in the synchronization queue; // The Thread held by the current Node references Node nextWaiter; // indicates the successor node in the condition queue // whether the current node status is SHARED mode final boolean isShared () {return nextWaiter = SHARED ;} // return the final Node predecessor () throws NullPointerException {Node p = prev; if (p = null) {throw new NullPointerException ();} else {return p ;}/// constructor 1 Node () {}// constructor 2. This constructor Node (Thread thread, Node mode) is used by default) {// note that the holding mode is assigned to nextWaiter this. nextWaiter = mode; this. thread = thread;} // constructor 3, used only in the condition queue Node (Thread thread, int waitStatus) {this. waitStatus = waitStatus; this. thread = thread ;}}

Node represents a Node in the synchronization queue and condition queue. It is an internal class of AbstractQueuedSynchronizer. Node has many attributes, such as holding mode, waiting status, synchronization queue's forward and backward, and condition queue's subsequent references. We can regard synchronization queue and condition queue as queuing areas, each node as a seat in the queuing area, and the thread as a waiting guest. When the guest arrives, he will first knock on the door to see if the lock is unlocked. If the lock is not unlocked, he will go to the queuing area to get a number card and declare the way he wants to hold the lock, then, the queue is queued at the end of the queue.

4. How do I understand the exclusive and sharing modes?

As mentioned above, each guest will receive a number card before queuing and declare the way in which they want to possess the lock. The way in which the lock is possessed is divided into the exclusive mode and the sharing mode, so how can we understand the exclusive and sharing modes? I can't find any good analogy. You can think of public toilets. People with an exclusive model are more aggressive. I am not allowed to enter again when I come in, occupy the entire toilet by yourself. People in the sharing mode are not so well-versed. When it finds that the restroom is ready for use, it does not matter if it comes in on its own. I have to be eager to ask the people in the background not to mind using it together, if the people behind the queue don't mind using them together, they don't have to queue up again. Of course, if the people behind them don't mind, they have to stay in the queue and continue to queue.

5. How do I understand the node Wait Status?

We also see that each node has a waiting status, which can be CANCELLED, SIGNAL, CONDITION, and PROPAGATE. You can think of this waiting status as a sign hanging next to the seat to identify the waiting status of the person in the current seat. You can modify the status of this brand. For example, if this thread has already planned to give up in the queue, it will set the sign on its seat to CANCELLED so that others can clear it out of the queue. Another situation is that when the thread is waiting to fall asleep in the seat, it will change the sign in the front position to SIGNAL if it is afraid that it has slept in the head, because each person will return to his seat and take a look before leaving the queue. If the status on the sign is SIGNAL, it will wake up the next person. The current thread can sleep with peace of mind only when the sign at the previous position is SIGNAL. The CONDITION status indicates that the thread is queued in the CONDITION queue. The PROPAGATE status reminds the thread that can directly obtain the lock. This status is only used in the sharing mode, the sharing mode will be discussed later.

6. What operations will be performed when a node enters the synchronization queue?

// Node team-up operation, returns the private Node enq (final node Node) {for (;) {// gets the Node t = tail referenced by the end Node of the synchronization queue; // if the end Node is empty, the synchronization queue has not initialized if (t = null) {// initialize the synchronization queue if (compareAndSetHead (new Node ())) {tail = head ;}} else {// 1. point to the current tail node. prev = t; // 2. set the current node to the end node if (compareAndSetTail (t, node) {// 3. point the successor of the Old Tail node to the new tail node t. next = node; // the unique exit of the for Loop return t ;}}}}

Note: an endless loop is used to join the queue. Only after nodes are successfully added to the end of the synchronization queue will the result be returned. The returned result is the original End Node of the synchronization queue. Demonstrate the entire operation process.

You need to pay attention to the order of adding the end node, which is divided into three steps: pointing to the end node, CAS changes the end node, and points the successor of the old end node to the current node. In the concurrent environment, these three steps may not be completed. Therefore, in the operation of clearing all canceled nodes in the synchronization queue, in order to find nodes in the non-canceled status, instead of traversing backward, it traverses backward. In addition, when each node enters the queue, its waiting status is 0. Only when the thread of the successor node needs to be suspended will the waiting status of the previous node be changed to SIGNAL.

Note: All the above analyses are based on JDK1.7, which may vary with different versions.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

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.