当前位置:网站首页>AQS source code analysis
AQS source code analysis
2022-06-24 22:21:00 【Nice2cu_ Code】
ReentrantLock Underlying principle
List of articles
One 、AQS brief introduction
- abstract queue synchronizer
AbstractQueuedSynchronizerabbreviation AQS, It is the basic component of synchronizer ,JUC The underlying implementation of various locks depends on AQS
1.1 Member variables

stateIndicates the lock status- The value is 0 No one holds the lock ( In a free state ),int Type defaults to 0
- Greater than 0 Indicates that a thread holds the lock
- In case of Lock reentry Then value +1
headandtailEach represents a point to the queue Head node and Tail node Of The pointerAQS The queue in is First in first out bidirectional linked list
- Be careful : The head node in the queue is not the node to acquire the lock , Just space occupying furnishings , The node that really wants to acquire the lock is the second node , The second node becomes the head node after acquiring the lock
- If a thread does not acquire a lock, it needs to enter the queue and wait
- The thread holding the lock must not be in the queue ( Remember the conclusion first , Analyze the reason in the source code )
1.2 Node node

- Each in the queue Node The node consists of four parts
- Front pointer 、 The back pointer 、 Current thread 、 The waiting state of the current thread
- about
WaitStatusEnumerated values , Record the of the current thread Wait state ,int Type. The default value is 0CANCELLED(1) Indicates that the thread has been canceledSIGNAL(-1) Indicates that the thread needs to be awakened , In a waiting stateCONDITION(-2) Indicates that the thread is waiting in the condition queuePROPAGATE(-3) Indicates that other nodes need to be notified when releasing shared resources
1.3 Inheritance relationships
AbstractQueuedSynchronizer Inherited from AbstractOwnableSynchronizer

exclusiveOwnerThread At present Lock thread

Two 、 Get lock source code analysis
Before the formal analysis , Need to know ReentrantLock There is a queue in , Threads that can't get the lock will enter the queue

This queue inherits AQS, in other words ReentrantLock Contains AQS queue

Official source code analysis :
Get into lock Method

Continue to enter lock Method

Select the implementation of fair lock

Get into acquire Method
The parameter value is fixed to 1, Indicates an attempt to AQS Properties in state Change it to 1( Lock )

Get into tryAcquire Method

Select the method of fair lock rewriting

The implementation class must override tryAcquire Method , Otherwise, an exception that does not support the operation will be thrown

First, get the thread currently trying to lock , Then enter getState Method

return state Value , This is the case 0( There is no thread lock yet )

getState Method returns 0, Judge that the lock is free , Get into if Code block , perform hasQueuedPredecessors Method
hasQueuedPredecessors Method is used to determine whether the current thread needs to wait in the queue
here , Maybe you have doubts , At this point, it has been determined that this thread can be locked , Directly through CAS Just lock it , Why judge whether you need to queue or not ?( View 2 、1 answer )

hasQueuedPredecessors Method returns false, Express You don't need to join the queue and wait

- At this time, the values of all three are null,return The first judgment of returns false, The entire method returns false
The source code is returned to section 9 Step , namely tryAcquire Method , Get into setExclusiveOwnerThread Method

Successfully set the locked thread to the current thread

Return the source code to section 11 Step ,tryAcquire Method returns true, The source code is returned to 5 Step , namely acquire Method

The source code continues to return , Until the first step calls lock method , The code continues to run down , Indicates that the locking is successful
Conclusion :
A single thread or multiple threads do not compete In alternation ,ReentrantLock stay JDK level You can solve the synchronization problem , It will not involve the call of the underlying interface of the operating system
Multiple threads do not compete and alternate execution will not use the queue , Just modify state Value
JDK1.6 Before synchronized Lock 、 The lock release operating system needs to switch to the kernel state , And call the underlying interface in the operating system , The operation is relatively heavyweight ,ReentrantLock Lighter than it
JDK1.6 Yes synchronized After optimization ( Biased locking 、 Lightweight lock ), about Single thread 、 Multithreading does not compete Can also be in JDK The layer is locked 、 Unlock operation , The performance of the two is almost the same
summary :
In the locking process, first judge state Value , If 0, Then judge whether to join the queue , If you don't need to , take state Value is modified to 1, And change the locked thread to the current thread , Locking success
2.1 Why judge whether you need to enter the queue ?
Why? tryAcquire Method has determined that this thread can be locked , You have to decide whether you need to queue up ?

- If t1 For the lock , here t2 Attempt to lock failed , Will enter the queue , At some point t1 Finished executing the code , Release the lock ,state The value of becomes 0, Right now t3 Attempt to acquire lock , Judge state The value of is 0, If you pass immediately CAS Lock ,t3 It will be better than t2 Get the lock first , Do not meet the characteristics of fairness
For unfair locks
If the current state The value is 0, be Try locking directly , Without judging whether you need to join the queue

3、 ... and 、 Lock Competition source code analysis
Lock thread t1 The lock hasn't been released yet , There are other threads t2 To try to lock , There is competition
t2 Try to lock , Consistent with the above source code process , To tryACquire Method
problem :ReentrantLock How to reflect the of lock reentry ?( Look at three 、1 answer )

Return the source code to the upper layer acquire Method
perform addWaiter Method , Queue threads that cannot acquire locks

perform enq Method

analysis enq Method
enq Method to create the head and tail nodes of the queue , Set the parameter node as the tail node , And form a two-way linked list with the head node :

You need to specify the in the queue at this time head and tail Values are null
Node t = tail;Assignment result t by nullGet into if Sentence judgment , Create a Thread is null Null node for value , And set it as the head node of the queue ( In the head node of the queue Thread Value is always null)

tail = head;This indicates that the head node also becomes the tail node
Back to the source code , Will not enter the else in , Again into the cycle , Judge that at this time t Not empty ,t It's the head node ( Tail node ), Get into else in

take contain t2 Thread Node node The previous node of is set as the head node , adopt CAS Action sets the tail node to contain t2 Thread Node node , The next node of the header node is set to contain t2 Thread Node node ( Double linked list ), Pictured :

here contain t2 Thread Node node Team success , Become the tail node
Return the source code to the next level addWaiter, Returns the... That becomes the tail node at this time contain t2 Thread Node node

Return the source code to the next level acquire Method , perform acquireQueued Method , Parameter is contain t2 Thread Node node ( Tail node ) and 1

here t2 Thread has been queued , But he has to spin to judge whether he can get the lock , Because it's possible t1 In the process of joining the team, he released the lock ,t2 There is no need to block ; If the spin still doesn't get the lock , So you're going to have to t2 Blocking ( Joining the team is not blocking )
Be careful : about t3 threads , The node before it in the queue is t2 node , Out of the principle of fairness , In any case, it won't be t3 Gets the lock , therefore t3 Can't spin , Only the thread corresponding to the second node will spin
Yes acquireQueued Method analysis
The thread corresponding to the second node Spin twice Backward entry blocking state , Wait for the thread to be released to wake up , Become the head node of the queue ( Head of the node Thread To be set to null), The thread corresponding to the second node successfully obtains the lock . The original first node is out of the queue , By GC Garbage collection :

final Node p = node.predecessor();p Become contain t2 Thread Node node Previous node of , Now it's Thread by null The head node ofif (p == head && tryAcquire(arg))The first condition is satisfied , The second condition is similar to the previous analysis , Try to spin once to get the lock , Suppose the acquisition fails at this time- It can be seen that , Only the second node will spin , The third node won't spin
Into the second if block , call
shouldParkAfterFailedAcquire, The first parameter represents the header node , The second parameter represents the tail node ( contain t2 Thread Node node )private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; // Head of the node status The value is int Type default 0 if (ws == Node.SIGNAL) return true; // Do not enter ,SIGNAL The value is -1 if (ws > 0) { // Do not enter do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // Get into // take node Previous node of ( Head node ) Of status It is amended as follows -1, Indicates a waiting state // Note not to modify t2 Threads node Node status compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; // return false }Return the source code to the next level , Keep going into the cycle

On the second cycle , hypothesis if In block , Another spin attempt to get the lock still didn't get the lock , Enter again
shouldParkAfterFailedAcquireMethodprivate static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; // Head of the node status The value is -1 if (ws == Node.SIGNAL) return true; // Get into , return true // Omit the rest ..... }At this point, there is a small problem , Why did you enter... For the first time
shouldParkAfterFailedAcquireWhen the method is used , Not directly ws==0 Just go back to true, You have to assign more values ( It is amended as follows -1) Well ?( Look at three 、2 answer )Get into
parkAndCheckInterruptMethod
parkAndCheckInterruptMethod , call park Method take t2 Thread blockingprivate final boolean parkAndCheckInterrupt() { LockSupport.park(this); // Blocking t2 Threads // The following code cannot be executed , Until another thread executes unpark Method wake up t2, Will execute down return sentence return Thread.interrupted(); }Suppose that at this time, the locking thread Lock released , And awakened t2,t2 perform
return Thread.interrupted();return fasleparkAndCheckInterruptMethod returns false, Keep going into the cycle
Go to the first if sentence , Attempt to acquire lock succeeded , Get into setHead Method
setHead Methods to analyze

Take the second node as the head node
In the second node Thread Be set to null(t2 Now hold the lock , The thread holding the lock must not be in the queue )
The pointer of the second node to the first node becomes null, Pictured :

Jump out of setHead Method , Back to the top , Keep going down

Change the pointer from the first node to the second node to null, The first node does not have any points at this time , By GC Garbage collection
renturn false,t2 Acquire the lock , The code continues to return , until lock Method , Keep going down

3.1 Embodiment of lock reentry
- When a thread executes to tryAcquire When the method is used , There is one if conditional , Compare the thread trying to acquire the lock with the thread holding the lock , If the same , On the other hand state Perform the operation of adding one , Indicates lock reentry
3.2 Why modify it twice waitStatus Value ?
- Why did you enter... For the first time
shouldParkAfterFailedAcquireWhen the method is used , Not directlyws==0Just go back to true To block , You have to assign more values ?- For one more spin ( The second node spins twice )
- Delay call park Time for ,park It will involve interface calls at the operating system level , It's a heavyweight lock , Consumption of resources
- state Each value of corresponds to a different state
- For one more spin ( The second node spins twice )
3.3 Interrupt when thread is blocked
If t2 The thread is shown below 1 The contents of the box are shown in , call park After method blocking , Interrupted by another thread , Then this method continues down , return true, The operation process is shown in the figure below :
Return after interrupt true Why , You can refer to another article of the blogger , Delivery address :interrupt、interrupted 、isInterrupted difference

You can find , When a thread is in a waiting queue , Cannot process external interrupt requests ( The thread is shown in the figure above 4 The box interrupts itself ), Only after the thread gets the lock , To handle external requests .
3.4 The third thread joins the queue
t3 The situation after the thread joins the queue is shown in the following figure

Combined with the previous source code, we can find , After each thread joins the queue status Values are 0,status The value of will be changed by the next thread joining the queue to -1, Not that you will status Is changed to -1, reason :
- Calling park Before , There is no modification status Value ,park after , Thread blocking , Naturally, you can't modify it yourself status Value
- If in park I modified it myself before status Value , If there is an exception , May lead to park Execution failure , Cannot block
Four 、 Unlock source code analysis

Get into :

Get into :

Back off , If the lock does not re-enter, then tryRelease The return value of the method is true:

If the waitStatus Values are not for 0, It means that a node is blocked after it , Need to be awakened ( In the header node waitStatus If the value of is -1, He needs to wake up the next node ):
// Parameters node Indicates the header node , This method is to wake up head Next node of , Let it spin to get a lock
private void unparkSuccessor(Node node) {
// here head Our mission has been completed , Just a placeholder virtual node , It needs to be status Set as 0, Will not affect the judgment of other functions
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
// Search forward from the end of the queue , Find out except head The front most outside the node and waitStatus Value less than or equal to 0d
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// After waking up, a blocked node spins to obtain a lock
if (s != null)
LockSupport.unpark(s.thread);
}
4.1 Why look for nodes from back to front ?
One way to unlock is to wake up head Next node of , Let it spin to get a lock , namely unparkSuccessor Method . In the wake-up process of this method , Is to search forward from the tail node of the queue , Why not search backwards from the beginning ?
answer :
In the process of competing for locks , Will execute a enq() Method , This method creates the head and tail nodes of the queue , Set the parameter node as the tail node , And form a two-way linked list with the head node :

In execution if(compareAndSetTail(t, node)) when ,cas It's atomic manipulation , cas After success ,tail Point to the head node (tail Of prev Pointer in cas It has been established before the operation ), A two-way linked list has not yet been formed .
When executed if The content in the code block , It's not an atomic operation , At this point, if other threads call unpark Method ( The header node has not been next Point to tail), You can't traverse the complete queue from scratch , And looking forward from the back .
边栏推荐
- TCP RTT测量妙计
- Short video mall system, how does scroll view adapt to the remaining height of the page
- 985测试工程师被吊打,学历和经验到底谁更重要?
- 短视频商城系统,scroll-view如何自适应页面剩余高度
- Main steps of system test
- 无心剑汉英双语诗003. 《书海》
- Rotate the square array of two-dimensional array clockwise by 90 °
- socket done
- Disk structure
- leetcode1863_ 2021-10-14
猜你喜欢

Collapse code using region

Servlet details

华大04a工作模式/低功耗模式

Information update on automatic control principle

Ideal L9, new trend of intelligent cockpit

How to extract dates from web pages?

String exercise summary 2

NIO、BIO、AIO

The process from troubleshooting to problem solving: the browser suddenly failed to access the web page, error code: 0x80004005, and the final positioning: "when the computer turns on the hotspot, the

Several schemes of traffic exposure in kubernetes cluster
随机推荐
interrupt、interrupted 、isInterrupted 区别
socket(1)
YGG 近期游戏合作伙伴一览
Want to be a test leader, do you know these 6 skills?
Stl+ tree
Junior college background, 2 years in Suning, 5 years in Ali. How can I get promoted quickly?
Binary search tree template
想当测试Leader,这6项技能你会吗?
Yida technology signed a contract with seven wolves to help the digital transformation of "Chinese men's wear leader"
Flutter-使用 typedef的注意事项
04A中断的配置
Opengauss kernel: simple query execution
String exercise summary 2
Information update on automatic control principle
Ideal L9, new trend of intelligent cockpit
TKKC round#3
AQS源码分析
Réduire le PIP à la version spécifiée (mettre à jour le PIP avec pycharm avant de le réduire à la version originale)
Docker 安装 Redis-5.0.12,详细步骤
Huada 04A operating mode / low power consumption mode