One: Understanding of CAS
CAS: compare and replace
Basic implementation: V-memory address A-old expected value, B-new value to be modified
Basic implementation process: When updating a variable, only when the old expected value A of the variable is the same as the value V of the memory address, V will be modified to the new value B to be modified
Graphical process
The cornerstone unsafe class implemented by CAS:
Unsafe: Mainly to keep the CAS atomic operation comparison and replacement actions consistent (officially not recommended and rarely used for operations)
Frequently Asked Questions ABA Questions
The general meaning is that the memory value V to be operated by CAS is changed from A to B and then changed to A, which leads to the successful execution of the subsequent execution that should not have been successfully executed.
demo
The solution is to add a version number to the data, such as ES does.
Two: Understanding of AQS
AQS (Abstract Queue Synchronizer):
1- is the basic framework for building locks or other synchronization components
2- It is the abstract implementation of the common basic part of various locks or synchronization components
3- Guarantee the management and maintenance of the synchronization queue
4- Guarantee the management of the synchronization status
5-Thread blocking and wake-up management
The basic design idea of AQS
- First encapsulate the competing threads and the waiting state into Node objects
- Put these node objects into a synchronous queue. This synchronous queue is a FIFO and a two-way queue is implemented based on the CLH queue.
AQS uses an int state to represent the synchronization state, such as: whether there is a thread to acquire the lock reentry times, etc. The specific meaning is defined by the specific subclass itself
Three: AQS source code analysis
In AQS, there is a waiting queue with node as the object. The clh queue is probably the following structure.
Node is to encapsulate the threads that come to compete for the lock and their states into a node object
There are Head and Tail nodes in AQS
Head node points to the first node1
Tail node points to the last node4
Between nodes, prev points to the previous node
Between nodes and nodes is next pointing to the next node
The main components of a node node
Source code analysis of the construction process of the synchronization queue
Assume that the current thread 0 has been occupied until the lock
A-Start entrance
Because thread 0 acquires the lock thread 1 enters the addWaiter method
This method first takes out the current thread 1 to generate a node1
Give the tail value of AQS to pred
tail=null so the if condition is not true
Enter the enq method with node1
inside a vicious circle
because tail=null
So into the first if statement
Create a new node0 and set the value of AQS Head to node0 and tail to point to node0
Judge tail again! =null
enter esle
current t = node0
while node.prev = t
So the prev of node1 points to node0
Change tail from pointing to node0 to pointing to node1 through compareAndSetTail
t.next = node point the next of node0 to node1
exiting the loop
Put back to the previous method
After the addWaiter method is executed, enter the acquireQueued method
interrupted is a status bit for judging whether it is interrupted
Take out the pre value of node1 to p, so the value of p is node0
node0 is the head trying to acquire the lock The current condition is that thread 0 occupies the lock
Assumption 1: Thread 0 completes the task to release the lock Thread 1 acquires the lock
Point the value of head from node0 to node1
The next of node0 is disconnected and points to node1
At this time, actually disconnect node0 from the queue and wait for the GC to recycle.
return interrupted
Assumption 2: Thread 0 is still using the lock or the lock is not acquired by thread 1 after thread 0 releases the lock but is acquired by the following thread
Then enter the second if
Enter shouldParkAfterFailedAcquire
Get the waitStatus value of node0 to 0
So go in the compareAnd... method and change ws to SIGNAL
Return to the infinite loop of the previous method and enter this method
Enter the current method again to get ws as SIGNAL, so enter the first method
Return true directly and return to the previous method to enter the parkAndCheckInterrupt method
The thread here directly parks thread 1 and waits for the thread to wake up
Analysis of the source code of the maintenance process during the operation of the synchronization queue
The current condition is that thread 0 acquires the lock until thread lock 0 completes the work and releases the lock
go this way
The conditions are not generated as kull and node0 is SIGNAL, enter the unparkSuccessor method
The value of SIGNAL is -1 so <0 Here the status of node0 is changed to 0
Go down and get node1 for s
Judging that the value of s is not NUll, enter unpark and wake up the previous thread 1 at this time
Queue exception source code analysis during synchronous queue operation
The maintenance method is in finall in the acquireQueued method
When a thread is interrupted or crashed
Processing source code of front node interrupt
first place
Enter cancelAcquire
private void cancelAcquire(Node node) {
if (node == null)
return;
//这里把当前的线程从这个node里面剔除
node.thread = null;
//取到node之前的线程
Node pred = node.prev;
//这里是如果node的前置节点node1也被取消
while (pred.waitStatus > 0)
//从node节点依次往前去找 直到找到没有被取消的node 把当前的node的pre值指向没有被取消的
node上
node.prev = pred = pred.prev;
Node predNext = pred.next;
//把自己的状态标记为CANCELLED
node.waitStatus = Node.CANCELLED;
//如果node是tail 把AQS的tail设置为node的前一个node1
if (node == tail && compareAndSetTail(node, pred)) {
//node的前置node1的next设置为null 到这里其实就是把这个node和队列断开了联系
compareAndSetNext(pred, predNext, null);
} else {
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
//这里就是把node的下一个node2拿到
Node next = node.next;
if (next != null && next.waitStatus <= 0)
//把node的前置node1的next指向node后置node2
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
//这里进行断开自己的node 同样就把自己从队列里面进行剔除
node.next = node; // help GC
}
}
second processing place
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//这里是如果node的前置node1中断了
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
//进行循环往前推找到一个没有中断的线程 把node的next指向那个正常的node
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
Handling source code of post node interrupt
place of first treatment
In release---------->unparkSuccessor
Node s = node.next;
//拿到node的后置node2
if (s == null || s.waitStatus > 0) {
s = null;
//把tail的值给了T 从后面逐步往前找 边走 边把t的值一直刷新为t的pre值
//如果刷新的这个t值的状态不为中断状态 则 s的值一直保持和t值一样
//如果刷新的这个t值的状态为中断状态 则 s的值不保持和t值一样
//直到跑到t为第一个node值 终止循环
//此时s为当前node之后第一个没有中断状态的node 把node的next指向这个s
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
Welcome to study and exchange, please point out the shortcomings, if you like it, please like + bookmark, thank you guys