This article is based on JDK-8U261 source code analysis
I believe most people know AQS because of ReentrantLock, the underlying ReentrantLock is implemented using AQS. There are some people know a Shared lock (Semaphore/CountDownLatch/CyclicBarrier) is implemented by AQS. That is to say, there are two modes in AQS: exclusive and shared. But do you think that’s all there is to AQS? It’s not. There is a third mode in AQS: conditional queues. Blocking queues in Java (ArrayBlockingQueue, LinkedBlockingQueue, etc.) are implemented by conditional queues in AQS. The exclusive and shared modes mentioned above are implemented by CLH queues in AQS.
So this series of AQS source analysis will be divided into three articles to push (exclusive mode/shared mode/conditional queue), and will go into the analysis of each line of source code, hoping to give you a comprehensive understanding of AQS. So first of all, this article will analyze the realization of exclusive mode in AQS.
1 introduction
AQS full name AbstractQueuedSynchronizer, can is a synchronizer framework for multithreaded access to a Shared resource. Another great concurrency framework designed by Doug Lea, AQS finally makes it possible to have a universal concurrency mechanism in Java. And you can inherit it and implement the methods in it to achieve the desired exclusive mode or shared mode, or blocking queues can also be easily implemented through AQS.
Some of the most commonly used concurrent utility classes, such as ReentrantLock, Semaphore, CountDownLatch, ArrayBlockingQueue, etc. (all of which were also written by Doug Lea), inherit AQS.
Doug Lea is one of the most important people to learn about concurrency frameworks in Java
Professor of computer Science at the State University of New York at Oswego and currently chair of the Computer Science Department, he specializes in concurrent programming and the design of concurrent data structures. He is a member of the Java Community Process Executive Committee, chairman of JSR 166, a fellow of the Association for Computing Machinery, and a winner of the Darl-Nygaard Prize, one of the most prestigious Prizes in European computing
Information from wikipedia: en.wikipedia.org/wiki/Doug_L…
Back to the subject, there are several important concepts in AQS:
-
State: records the locking times of reentrant locks.
-
Inherited AbstractOwnableSynchronizer exclusiveOwnerThread: AQS, and one attribute exclusiveOwnerThread, who is used to record the current thread an exclusive lock.
-
CLH synchronous queue: FIFO bidirectional linked list queue, this CLH queue is a variant of the original CLH, from the original continuous spin to block mechanism. There are two Pointers in the queue: the head node and the tail node. The tail node points to the last node, while the head node always points to an empty node for easy judgment, followed by the first node with data.
-
Conditional queuing: Allows some threads to wait together for a condition to be met before waking up and then being placed in the CLH queue to re-compete for lock resources.
AQS defines two ways to access resources:
-
Exclusive mode: only one thread can acquire the lock, such as ReentrantLock.
-
** Shared mode: ** Multiple threads can acquire locks at the same time, such as Semaphore, CountDownLatch and CyclicBarrier.
AQS uses the template method pattern, provides some methods for subclasses to implement, subclasses only need to implement these methods, as for the specific queue maintenance does not need to care, AQS has been implemented.
1.1 the Node
The CLH queue and conditional queue nodes mentioned above are constructed by an inner Node class of AQS, which defines some Node attributes:
1 static final class Node {
2 /** 3 * marks the node for shared mode 4 */
5 static final Node SHARED = new Node();
6 /** 7 * marks the node for exclusive mode 8 */
9 static final Node EXCLUSIVE = null;
10 /** 11 * indicates that the node is in the cancelled state. Threads waiting for timeout or interrupted in the CLH queue need to be removed from the CLH queue
13 static final int CANCELLED = 1;
14 /** 15 * This state is special. If the next node of this node is blocked, the node is in SIGNAL state 16 * so this state indicates whether the next node is blocked, not the current state 17 */
18 static final int SIGNAL = -1;
19 /** 20 * Nodes in this state will be placed in the conditional queue 21 */
22 static final int CONDITION = -2;
23 /** 24 * is used in shared mode to indicate that nodes can wake up propagation. At this time, CLH queue does not need to wait for the previous node to release the lock, and then the node can obtain the lock. In the shared mode, all nodes in this state can obtain the lock, and the action of propagation wake-up is to realize the 26 */ by marking the PROPAGATE state
27 static final int PROPAGATE = -3;
28 /** 29 * Records the current node status. In addition to the preceding four states, there is also an initial state 0 30 */
31 volatile int waitStatus;
32 /** 33 * used in CLH queue to represent the previous node 34 */
35 volatile Node prev;
36 /** 37 * used in CLH queue to represent the latter node 38 */
39 volatile Node next;
40 /** 41 * Records the current blocked thread 42 */
43 volatile Thread thread;
44 /** 45 * used in conditional queues to represent the next node 46 */
47 Node nextWaiter;
48
49 / /...
50 }
Copy the code
1.2 the CLH queue
One thing to note here is that the HEAD pointer in the CLH queue will always point to an empty node. If the current node is culled and the next node becomes the first node, the contents of the node are cleared (waitStatus is not cleared) and the head pointer points to it. The purpose of this is to facilitate the judgment.
2 already overview
In exclusive mode, only one thread can acquire the lock resource. For example, in exclusive mode, ReentrantLock uses sync internally to inherit AQS. There are two types of fair and unfair locks:
1 public class ReentrantLock implements Lock.Serializable {
2
3 / /...
4
5 /**
6 * 内部调用AQS
7 */
8 private final Sync sync;
9
10 /** 11 * inherits AQS synchronization base class 12 */
13 abstract static class Sync extends AbstractQueuedSynchronizer {
14 / /...
15 }
16
17 /** 18 * unfair lock 19 */
20 static final class NonfairSync extends Sync {
21 / /...
22 }
23
24 /** 25 * fair lock 26 */
27 static final class FairSync extends Sync {
28 / /...
29 }
30
31 /** 32 * Creates an unfair lock object 33 */ by default
34 public ReentrantLock(a) {
35 sync = new NonfairSync();
36 }
37
38 /** 39 * Creates a fair lock or an unfair lock
41 public ReentrantLock(boolean fair) {
42 sync = fair ? new FairSync() : new NonfairSync();
43 }
44
45 / /...
46 }
Copy the code
Fair and unfair lock difference
AQS designs queues for all threads that do not acquire locks, so why choose queues instead of using Set or List structures? Because queues have FIFO first-in, first-out characteristics, that is, natural fairness characteristics, so in ReentrantLock only fair and unfair these two characteristics exist.
3 Unfair lock
3.1 the lock method
ReentrantLock non-fair lock lock method:
1 /** 2 * ReentrantLock: 3 */
4 public void lock(a) {
5 sync.lock();
6 }
7
8 final void lock(a) {
9 ExclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread exclusiveOwnerThread 12 doesn't have to be queued in the CLH queue blocking 13 */
14 if (compareAndSetState(0.1))
15 setExclusiveOwnerThread(Thread.currentThread());
16 else
17 // If it fails, the CLH queue will be blocked
18 acquire(1);
19 }
Copy the code
3.2 acquire method
In the above lock method, if the lock fails, the acquire method is queued. But first we try to get a resource:
1 /** 2 * AbstractQueuedSynchronizer: 3 */
4 public final void acquire(int arg) {
5 // First try to get the resource, and if that fails add a new exclusive node to the end of the CLH queue
6 if(! tryAcquire(arg) &&7 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
8 /* 9 since this method does not respond to interrupts, if the currentThread is awakened after an interrupt, the interrupt flag is reset to true 10. After calling the lock method for interruption, the user first checks with the isInterrupted method to see if the following business code should be executed
13 selfInterrupt();
14 }
15
16 /** 17 * ReentrantLock: 18 * Line 6:19 * Attempt to obtain a resource 20 */
21 protected final boolean tryAcquire(int acquires) {
22 return nonfairTryAcquire(acquires);
23 }
24
25 final boolean nonfairTryAcquire(int acquires) {
26 //acquires = 1
27 final Thread current = Thread.currentThread();
28 int c = getState();
29 // If there is no current lock
30 if (c == 0) {
31 // Try CAS to change state to 1
32 if (compareAndSetState(0, acquires)) {
33 // Set the current exclusive lock owner to the current thread
34 setExclusiveOwnerThread(current);
35 return true;
36 }
37 }
38 // If the current state is not 0, then the current thread is not the previous lock thread
39 else if (current == getExclusiveOwnerThread()) {
40 // If so, it is a reentrant lock and state+1
41 int nextc = c + acquires;
42 // If the value is negative after +1, an Error is raised
43 if (nextc < 0)
44 throw new Error("Maximum lock count exceeded");
45 setState(nextc);
46 return true;
47 }
48 return false;
49 }
Copy the code
3.3 addWaiter method
If the tryAcquire method still fails to acquire the resource, the addWaiter method is called to add a new node to the CLH queue:
1 / * * 2 * AbstractQueuedSynchronizer: 3 * CLH queue to add a new node 4 * / exclusive end
5 private Node addWaiter(Node mode) {
6 // Build the current thread as a new node
7 Node node = new Node(Thread.currentThread(), mode);
8 Node pred = tail;
9 // Check whether the current tail node is null. If the value is not null, there are nodes in the queue
10 if(pred ! =null) {
11 // Insert the current node as a tail
12 node.prev = pred;
13 //CAS points the tail node to the current node
14 if (compareAndSetTail(pred, node)) {
15 pred.next = node;
16 return node;
17 }
18 }
19 // If the queue is empty, initialize the queue and insert it into the current node
20 enq(node);
21 return node;
22 }
23
24 private Node enq(final Node node) {
25 /* 26 There are many CAS failures in high concurrency scenarios, and the following infinite loop ensures that nodes must be queued. The code above is similar to the code in the 27 ENQ method, which means that the above operation is for quick changes. If it fails, the enQ method will do the bottom 28 */
29 for (; ;) {
30 Node t = tail;
31 // If the tail node is null, the CLH queue is empty and needs to be initialized
32 if (t == null) {
33 // Create an empty Node Node and point the head CAS to it
34 if (compareAndSetHead(new Node()))
35 // Also point the last node to the new node
36 tail = head;
37 } else {
38 // If the CLH queue is not empty at this point, the node is inserted tail-inserted as before
39 node.prev = t;
40 if (compareAndSetTail(t, node)) {
41 t.next = node;
42 return t;
43 }
44 }
45 }
46 }
Copy the code
3.4 acquireQueued method
This method is the core method in AQS, which requires special attention:
1 AbstractQueuedSynchronizer: / * * 2 * 3 * note: this method is the essence of the AQS, completed the resources and other attempts to acquire lock head node 4 * / blocked all process
5 final boolean acquireQueued(final Node node, int arg) {
6 boolean failed = true;
7 try {
8 boolean interrupted = false;
9 for(; ;) {10 // Get the previous node of the current node
11 final Node p = node.predecessor();
12 /* if the first node in the CLH queue is the first node in the CLH queue, only the first node in the CLH queue can attempt to acquire the lock resource (FIFO). If the first node in the CLH queue is the first node in the CLH queue, the lock resource (FIFO) will not be blocked. The previous resource has been freed 16 */
17 if (p == head && tryAcquire(arg)) {
18 /* 19 the head pointer points to the current node, which means that the node will become an empty node (the head node will always point to an empty node) 20 Since the node in the CLH queue can be released if the tryAcquire method on the previous line has succeeded 21 */
22 setHead(node);
23 // Break the next pointer to the previous node so that it becomes an isolated node waiting to be GC
24 p.next = null;
25 failed = false;
26 return interrupted;
27 }
28 /* the previous node is either a head node or a head node and the attempt to lock failed. At this point, some CANCELLED nodes before the current 30 nodes in the queue will be deleted; If the previous node state is SIGNAL, it blocks the current thread. 31 The parkAndCheckInterrupt blocking operation here makes sense. Because without blocking, 32 threads that can't get resources could run in an infinite loop, consuming 33 */ of CPU resources
34 if (shouldParkAfterFailedAcquire(p, node) &&
35 parkAndCheckInterrupt())
36 InterruptedException is not thrown. That is, it does not respond to interrupts
37 interrupted = true;
38 }
39 } finally {
40 if (failed)
41 // If state+1 in tryAcquire overflows, the current thread's request to acquire the lock resource is cancelled
42 cancelAcquire(node);
43 }
44 }
45
46 /** 47 * Line 22:48 * Sets node as the new head and empties its thread and prev properties 49 * (note: this does not empty the waitStatus value) 50 */
51 private void setHead(Node node) {
52 head = node;
53 node.thread = null;
54 node.prev = null;
55 }
56
57 /** 58 * at line 34:59 */
60 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
61 int ws = pred.waitStatus;
62 if (ws == Node.SIGNAL)
63 // If the state of the previous node is SIGNAL, it means that the current node can safely block
64 return true;
65 if (ws > 0) {
66 /* 67 Search for a non-cancelled node (that is, a node in the normal blocking state). If a CANCELLED node is encountered during the 68 iteration, it will be removed from the CLH queue and wait for GC 69 */
70 do {
71 node.prev = pred = pred.prev;
72 } while (pred.waitStatus > 0);
73 pred.next = node;
74 } else {
75 /* 78 If the previous node is in the initial state 0 or PROPAGATE state PROPAGATE, CAS changes its state to SIGNAL, 77 Because the current node is blocked eventually, so the state of the previous node must be changed to SIGNAL. Because there is an infinite loop outside, if we can still jump into this method, 79 if the CAS was successfully modified, we will go straight into the first if condition and return true. Then the current thread is blocked. 80 If the CAS fails, it will enter the branch again for modification. 81 */
82 compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
83 }
84 return false;
85 }
86
87 /** 88 * line 35:89 * blocks the current node, and the node will be woken up from line 97 if unpark wakes it up, returning false. 90 * It is possible that the thread will be woken up by an interrupt while waiting, so this method returns true. At this point, the thread will be in an incorrect state. 91 * Will go back and set the interrupt bit to true at line 37, and finally go back. Note that line 110 below uses the 92 * Thread.interrupted method, which clears the interrupted status after returning true, So you need to reset the interrupt flag bit to true 94 */ by calling the Interrupt method in the acquire method 93 * above
95 private final boolean parkAndCheckInterrupt(a) {
96 // The current thread blocks at this line of code, stops running, and waits for unpark to wake up
97 LockSupport.park(this);
98 /* 99 After the interruption method is interrupted, the value of this method will be set to true. So why not just call isInterrupted at this point? You don't have to clear the interrupt flag 101, okay? The locksupport. park implementation calls the 102 Native method. Check the park method in the underlying HotSpot source for a reason: If the current interrupt flag 103 is true when the park method is called, the method will return (without clearing the interrupt flag) and no further 104 lines will be suspended. If Thread. Interrupted 105 is used to clear the interrupt bit, the next time the lock fails, the park method will still be used with the interrupt bit 106 true. However, as mentioned above, entering the park method will not be blocked, which means that the park method will fail and continue to spin in the 107 acquireQueued method, causing CPU spikes. So the thread. interrupted method clears the 108 flag so that subsequent calls to the Park method can continue to block 109 */ successfully
110 return Thread.interrupted();
111 }
Copy the code
3.5 cancelAcquire method
When an exception occurs, the cancelAcquire method is called to handle the exception, along with some other finishing touches. One important point is that if the node that needs to be woken up fails, the next node needs to be woken up to ensure that the wake action is propagated.
1 AbstractQueuedSynchronizer: / * * 2 * 3 * to cancel the current thread lock resources for requests, and complete the other 4 * / finishing touches
5 private void cancelAcquire(Node node) {
6 // Non-null check
7 if (node == null)
8 return;
9
10 // The thread inside the node is cleared
11 node.thread = null;
12
13 /* 14 Search for a non-cancelled node (i.e. a node that is normally blocked), 15 is equivalent to doing a clean up before quitting. If met CANCELLED node traversal process, will be out of 16 CLH queue waiting for the realization of the GC 17 here is logical and shouldParkAfterFailedAcquire method is similar, but have a bit of 18 different is: There is no pred.next = node, instead it is deferred to 19 */ later in the CAS operation
20 Node pred = node.prev;
21 while (pred.waitStatus > 0)
22 node.prev = pred = pred.prev;
23
24 /* 25 If CANCELLED nodes have been CANCELLED, predNext points to the next CANCELLED node
28 Node predNext = pred.next;
29
30 /* 31 Cancel the status. It is ok to change the state without CAS, since the CANCELLED state is changed at 32, and the CANCELLED node will be skipped at 33 */
34 node.waitStatus = Node.CANCELLED;
35
36 if (node == tail && compareAndSetTail(node, pred)) {
37 // If the current node is the last node, the current node is removed and the tail pointer points to the previous node
38 compareAndSetNext(pred, predNext, null);
39 } else {
40 int ws;
41 // The current node is not the last node
42 if(pred ! = head &&43 ((ws = pred.waitStatus) == Node.SIGNAL ||
44 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
45pred.thread ! =null) {
46 /* 48 If the head pointer does not point to a pred node, the previous node is SIGNAL (or can be set to SIGNAL), and the thread on the previous node is not cleared. All you need to do is concatenate the pred node to the next node after the current node
50 Node next = node.next;
51 if(next ! =null && next.waitStatus <= 0)
52 /* 53 Set next pointer to pred, not next-prev = pred. But just as well, in the subsequent operations, 54 if you get into shouldParkAfterFailedAcquire method, will amend prev pointer to 55 * /
56 compareAndSetNext(pred, predNext, next);
57 } else {
58 /* 59 If the head pointer points to a pred node (or pred thread is null), the CLH queue can be woken up even if an exception occurs. If the previous node itself is in SIGNAL state, it is also 62 */ that needs to wake up the next node
63 unparkSuccessor(node);
64 }
65
66 /* 67 node.next points to itself, disconnect the node, and ensure that the next pointer has a value. If node.next is null, then the node is still in the CLH queue 71 */
72 node.next = node;
73 }
74 }
Copy the code
3.6 unparkSuccessor method
This method is called not only in the above cancelAcquire method, but also in the unlock method to wake up the next node:
1 / * * 2 * AbstractQueuedSynchronizer: 3 * wake up the next node 4 * / can be awakened
5 private void unparkSuccessor(Node node) {
6 int ws = node.waitStatus;
7 /* 8 If the current node state is SIGNAL or PROPAGATE, set its CAS to the initial state 0 9 Because the first blocked node will be woken up later, so the node state is not correct if it is SIGNAL. 10 Because SIGNAL indicates that the next node is blocked 11 */
12 if (ws < 0)
13 compareAndSetWaitStatus(node, ws, 0);
14
15 //s is the next node of the current node
16 Node s = node.next;
17 // If the next node is null, or the status is CANCELLED
18 if (s == null || s.waitStatus > 0) {
19 s = null;
20 // Walk from the end of the CLH queue to the end of the CLH queue to find the first node after that node that is normally blocked
21 for(Node t = tail; t ! =null&& t ! = node; t = t.prev)22 if (t.waitStatus <= 0)
23 s = t;
24 }
25 Wake up if the next node before it is found or traversed is itself normally blocked
26 if(s ! =null)
27 LockSupport.unpark(s.thread);
28 }
Copy the code
3.7 unlock method
ReentrantLock unlock:
1 /** 2 * ReentrantLock: 3 */
4 public void unlock(a) {
5 sync.release(1);
6 }
7
8 /** 9 * AbstractQueuedSynchronizer: 10 */
11 public final boolean release(int arg) {
12 // Release the lock once, if there is no reentrant lock, enter the following if condition
13 if (tryRelease(arg)) {
14 Node h = head;
15 / * 16 if head node exists and the next node in the blocking state when they wake up the next node for shouldParkAfterFailedAcquire in before locking method method, If waitStatus is 0, it means that the next node is blocked, and then it can wake up. If waitStatus is 0, there is no need to wake up because the next node is itself non-blocked. 20 */
21 if(h ! =null&& h.waitStatus ! =0)
22 unparkSuccessor(h);
23 return true;
24 }
25 return false;
26}
27
28 /** 29 * ReentrantLock: 30 * line 13:31 */
32 protected final boolean tryRelease(int releases) {
33 //c = state - 1
34 int c = getState() - releases;
35 // If the current thread is not the locked thread, an exception is thrown
36 if(Thread.currentThread() ! = getExclusiveOwnerThread())37 throw new IllegalMonitorStateException();
38 boolean free = false;
39 // If state is 0 after subtracting 1, i.e. no reentrant lock occurs, then exclusive lock owner can be set to NULL
40 if (c == 0) {
41 free = true;
42 setExclusiveOwnerThread(null);
43 }
44 // Set state to the result of subtracting 1
45 setState(c);
46 return free;
47 }
Copy the code
4 fair lock
There are few differences between fair and unfair lock implementations of ReentrantLock, except that the Lock method is different from the tryAcquire method (including the implementation of unlock), which is overwritten in the FairSync class. So let’s look at the implementation of these two methods.
4.1 the lock method
1 /** 2 * ReentrantLock: 3 */
4 final void lock(a) {
5 /* 6 You can see that in fair lock mode, only the acquire method is called. In non-fair lock mode, 7 compareAndSetState is executed first, and acquire method is called only if CAS fails. This means that each thread in unfair lock 8 will try to lock first, betting on whether the lock will be released. If released, 9 then the thread can grab the lock, which is equivalent to cutting in line (which is what "unfair" means). If not, 10 continue to queue in CLH queue. In fair lock mode, every thread will just queue up when it locks
12 acquire(1);
13 }
Copy the code
4.2 tryAcquire method
1 /** 2 * ReentrantLock: 3 * You can see the difference between the tryAcquire method in fair lock mode and the nonfairTryAcquire method in non-fair lock mode. So let's look at the implementation of the 5 * method 6 */
7 protected final boolean tryAcquire(int acquires) {
8 final Thread current = Thread.currentThread();
9 int c = getState();
10 if (c == 0) {
11 if(! hasQueuedPredecessors() &&12 compareAndSetState(0, acquires)) {
13 setExclusiveOwnerThread(current);
14 return true;
15 }
16 } else if (current == getExclusiveOwnerThread()) {
17 int nextc = c + acquires;
18 if (nextc < 0)
19 throw new Error("Maximum lock count exceeded");
20 setState(nextc);
21 return true;
22 }
23 return false;
24 }
Copy the code
4.3 hasQueuedPredecessors method
The Hasqueued24 method is called at line 11 in the tryAcquire method above, so let’s look at its implementation:
1 /** 2 * ReentrantLock: 3 * This method is used to check whether there are any other nodes in the CLH queue that are not in the current thread, 4 * Because the CLH queue is FIFO, the head. If there is a thread in the CLH queue that waits longer to acquire the lock than the current thread. If it does, the current thread will not proceed with the subsequent lock operation (again, "fair" meaning). If it does not have 8 *, it will attempt to lock 9 */
10 public final boolean hasQueuedPredecessors(a) {
11 Node t = tail;
12 Node h = head;
13 Node s;
14 /* 15 <1> Head and tail are both null, or head and tail 16 point to the empty node (when only two nodes are left (one empty node and one node that is actually waiting), wake up node 17 and there will be only one empty node left in the CLH queue). Either way, it means that there are no 18 nodes blocking in the CLH queue at this point, so the current thread can try to lock; 19 <2.1> Check whether head. Next is empty if there are nodes in the CLH queue. One extreme scenario I can think of is: 20 Assuming that there is only one empty node in the CLH queue (both head and tail point to it) and that a new node needs to enter the CLH queue at the moment, 21 it goes to the addWaiter method and after executing compareAndSetTail, But the following "pred.next = node;" is not executed. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Instead, queue 24 in the CLH queue (in which case h.ext is null, but there is a node that has waited longer than the current thread, but whose pointer has not yet been connected in time to 25). So the current node will continue to queue in order to reflect the meaning of "fairness"); 26 <2.2> If there is a node in the CLH queue and it is not a special case in condition 2.1 above, head. Next 27 is the current thread. The scenario is that the current thread will be in the CLH queue at head.next, and then the current thread will decide again in the 28 methods. So how did this happen? One possible scenario: AcquireQueued (tryAcquire) ¶ After the thread that held the lock completes execution and is released, the queue header will be awakened and go to the tryAcquire method in the acquireQueued method. CurrentThread () = thread.currentThread (); If head.next is not the current thread, that is, the current thread is not the one waiting the longest. 32 The current thread will not lock, but queue
34 returnh ! = t &&35 ((s = h.next) == null|| s.thread ! = Thread.currentThread());36} The next article will continue to analyze the implementation of the sharing pattern in AQS, please pay attention. Line up below if you want to make fun of Doug LeaCopy the code
The longer I work in this industry, the more I feel: great oaks from little acorns grow, it’s a truth! In the application business for too long many bottom things are often easy to ignore, the plan at the beginning of this year is to do a summary of the commonly used JDK source tools, see the end of the year is approaching, by the recent free, hurriedly to make up.
- Do you know ArrayList? Describe the difference between remove for foreach and iterator
- Have you ever wondered why Internet companies always ask about collections? Let’s talk about the classic data structure HashMap
- AQS source code in depth analysis of exclusive mode -ReentrantLock lock feature details
- AQS source code for in-depth analysis of sharing mode – Why PROPAGATE state in AQS? (In process of creation)
- AQS source code in depth analysis of conditional queue -Java blocking queue is how to achieve? (In process of creation)
- AQS source code in-depth analysis of the application tool CountDownLatch (creation)
- CyclicBarrier: AQS source code analysis tool
- ConcurrentHashMap is a bug in Java 8. And there’s more than one! This pit is still relatively large, will focus on the summary behind! (Finished)
- ThreadPoolExecutor source code analysis – The implementation process of the Java thread pool is broken, and many people are still confused. (Finished)
- ScheduledThreadPoolExecutor source analysis – often timer thread pool focus on how to achieve delay the execution and cycle!
- ThreadLocal source code analysis – key summary, memory leak, soft reference weak reference false reference, interview often like to ask, I also like to ask another
- Red black tree TreeMap, LinkedHashMap
- An in-depth understanding of the ordered and threaded Map container ConcurrentSkipListMap
- LinkedList (Not sure if you want to write it, if you have time, it depends on the project)
- 1T data quicksort! Summary of ten classical sorting algorithms
Each summary is the knowledge of the degree of examination, technology is not easy, every day a little better, with everyone.
In addition, the author’s public account: Geek time, there are more wonderful articles, interested students, you can pay attention to