Java concurrent packet source code learning-AQS framework (2) CLH lock queue and spin lock, aqsclh
As mentioned in the previous article, AQS is based onCLH lock queue
So what is CLH lock queue? complexity is complicated, simple, and simple. The so-called greatest truths are simple:
CLH lock queue is actually a FIFO queue, and each node (thread) in the queue only needs to wait for its previous step to release the lock.
AbstractQueuedSynchronizer
Is through an internal classNode
To implement a variant of CLH lock queue, but the basic principle is similar.
In IntroductionNode
Class, let's introduceSpin Lock
GenerallyCLH lock queue
To implement the spin lock, the so-called spin lock is simply a thread waiting through a loop rather than sleep. Talk is much worse than show code:
Class ClhSpinLock {private final ThreadLocal <Node> prev; private final ThreadLocal <Node> node; private final AtomicReference <Node> tail = new AtomicReference <Node> (new Node ()); public ClhSpinLock () {this. node = new ThreadLocal <Node> () {protected Node initialValue () {return new Node () ;}}; this. prev = new ThreadLocal <Node> () {protected Node initialValue () {return null ;};} public void lock () {final Node node = this. node. get (); node. locked = true; // a CAS operation can add the node corresponding to the current thread to the queue, // and get the reference of the forward node at the same time, then, wait for the previous step to release the lock Node pred = this. tail. getAndSet (node); this. prev. set (pred); while (pred. locked) {// enter the spin} public void unlock () {final Node node = this. node. get (); node. locked = false; this. node. set (this. prev. get ();} private static class Node {private volatile boolean locked ;}}
In the above Code, the thread cleverly passesThreadLocal
Saves the reference of the current node and the forward node. Spin is the while loop in the lock. In general, the benefit of this implementation is to ensure fair competition among all waiting threads, and not to compete with the same variable, because each thread only needs to wait for its own release. The advantage of spin is that the thread does not need to sleep or wake up, reducing the overhead of system calls.
public static void main(String[] args) { final ClhSpinLock lock = new ClhSpinLock(); lock.lock(); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { lock.lock(); System.out.println(Thread.currentThread().getId() + " acquired the lock!"); lock.unlock(); } }).start(); Thread.sleep(100); } System.out.println("main thread unlock!"); lock.unlock();}
The running result of the code above should be exactly the same as that in the previous article.
ClhSpinLock
The implementation of the Node class is very simple with only one Boolean value,AbstractQueuedSynchronizer$Node
The implementation is a little complicated, probably like this:
+------+ prev +-----+ +-----+head | | <---- | | <---- | | tail +------+ +-----+ +-----+
- Head: Header pointer
- Tail: tail pointer
- Prev: pointer to forward
- Next: This pointer graph is not shown. It is opposite to prev and points to the next
The key difference is the next pointer. This is because the thread in AQS is not always spinning, but may sleep and wake up repeatedly, this requires the next pointer to find the next pointer to wake up the lock, that is, the AQS waits for the queue to be awakened by the next pointer. AQS combines the advantages of spin and sleep/Wakeup methods.
Thread sleep and wake-up are used in my next article.LockSupport
.
Last, the aboveClhSpinLock
Another key point in the class islock
Annotations in the method:
A cas operation can add the node corresponding to the current thread to the queue and obtain its successor.
In fact, the entire AQS framework is based on CAS, and these atomic operations are the core of multi-thread competition. Many of AQS's code is bypassed to reduce competition. I will be behindAbstractQueuedSynchronizer
The source code analysis is described in detail.