Java concurrent programming, tiktok rear end technology 3 faces

Keywords: Java node.js Back-end Programmer

    }
}
//If the tail node is null, enq is used to join the queue
enq(node);
return node;

}

//The synchronizer ensures the correct addition of nodes through an endless loop. After the node is set as the tail node through CAS in the "endless loop",
//The current thread can only return from this method. Otherwise, the current thread keeps trying to set.
private Node enq(final Node node) {
//CAS "spin" until you successfully join the tail of the team
for (;😉 {
//Tail node temporary storage
Node t = tail;
if (t == null) { // Must initialize
//If tail is null, the
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}

stay `addWaiter(Node node)` Method to add the current thread node to the waiting queue.

![enq.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b455d93981d411e865319643c5e5f57~tplv-k3u1fbpfcp-watermark.image)

##### 2\. acquireQueued()

**`acquireQueued` The thread in the queue acquires the lock**

/**

  • Acquires in exclusive uninterruptible mode for thread already in
  • queue. Used by condition wait methods as well as acquire.
  • @param node the node
  • @param arg the acquire argument
  • @return {@code true} if interrupted while waiting
  • Acquirequeueueued method the current thread obtains the synchronization status in an endless loop. Only the precursor node is the head node can attempt to obtain the synchronization status (lock) (P = = head & & tryacquire (ARG))
  • as a result of:1.The head node is the node that successfully obtains the synchronization state (lock). After the thread of the head node releases the synchronization state, it will wake up its successor node. After the thread of the successor node is awakened, it is necessary to check whether its predecessor node is the head node.
    
  •       2.Maintaining synchronization queues FIFO In principle, after the node enters the synchronization queue, it enters a spin process, and each node (or each thread) is introspecting and observing.
    

*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//The deadlock check (spin check) checks whether the predecessor node of the current node is the head node to obtain the lock
for (;😉 {
//Gets the precursor node of the node
final Node p = node.predecessor();
If (P = = head & & tryacquire (ARG)) {/ / check the thread loop in the node to see if its own precursor node is the head node
//Set the current node as the head node and remove the previous head node
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//Otherwise, check the status of the previous node to see if the thread that failed to acquire the lock should be suspended
if (shouldParkAfterFailedAcquire(p, node) &&
//If you need to suspend, use the static method park of LockSupport class under the JUC package to suspend the current thread until it is awakened
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//If there are exceptions
if (failed)
//Cancel the request and remove the current node from the queue
cancelAcquire(node);
}
}

adopt `addWaiter` Method is added to the waiting queue `acquireQueued` Method to obtain the lock.

The general process is as follows:

*   `tryAcquire()` Try to obtain resources directly, and return directly if successful;
*   `addWaiter()` Add the thread to the tail of the waiting queue and mark it as exclusive mode;
*   `acquireQueued()` Causes the thread to obtain resources in the waiting queue and returns only after obtaining the resources. If it is interrupted during the whole waiting process, it returns `true`,Otherwise return `false`. 
*   If a thread is interrupted while waiting, it will not respond. It will only interrupt itself after obtaining resources `selfInterrupt()`,Make up the interruption.

##### 3 \. Exclusive lock acquisition process

Calling the synchronizer `acquire(int arg)` Method can obtain the synchronization status. This method is not sensitive to interrupt, that is, after the thread fails to obtain the synchronization status, it enters the synchronization queue. During subsequent interrupt operations on the thread, the thread will not be removed from the synchronization queue. Acquisition process:

1.  Current thread passed `tryAcquire()` Method attempts to obtain a lock. If it succeeds, it returns directly. If it fails, it enters the queue and waits `CAS` Gets the synchronization status.
2.  If the attempt to acquire the lock fails, the synchronization node (exclusive) is constructed `Node.EXCLUSIVE`),adopt `addWaiter(Node node,int args)` method,Add the node to the end of the synchronization queue.
3.  Last call `acquireQueued(final Node node, int args)` Method to make the node obtain the synchronization status in an endless loop. If it cannot obtain the synchronization status, the threads in the node will be blocked.`acquireQueued` Method the current thread obtains the synchronization state in an endless loop. Only when the precursor node is the head node can it attempt to obtain the lock (synchronization state)( `p == head && tryAcquire(arg)`). 

![lock.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3780d99d1e73401f9f51a4276db2d08c~tplv-k3u1fbpfcp-watermark.image)

#### 2.2.2 release(int) release of exclusive lock

stay `AQS` Passed in `release` Method to release the lock.

public final boolean release(int arg) {
//Call the tryRelease method to release
if (tryRelease(arg)) {/ / if the release succeeds
Node h = head;
//If the header node is not null, and the waitStatus value of the header node is not 0, there is a status
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
//If the release is not successful, false is returned
return false;
}

//tryRelease() attempts to release the synchronization state (lock) of the current thread
protected final boolean tryRelease(int releases) {
//c is the synchronization state after release
int c = getState() - releases;
//Judge whether the thread that releases the lock is the thread that obtains the lock (synchronization status) instead of throwing an exception (illegal monitor status exception)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//If the lock (synchronization state) has been completely released by the current thread, set the lock holder to null and the synchronization state (lock) becomes available
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState©;
return free;
}

private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);

/*
 * Thread to unpark is held in successor, which is normally
 * just the next node.  But if cancelled or apparently null,
 * traverse backwards from tail to find the actual
 * non-cancelled successor.
 */
Node s = node.next;
//Start at the end of the queue and move forward to find the first node with waitStatus less than 0.
if (s == null || s.waitStatus > 0) {
    s = null;
    for (Node t = tail; t != null && t != node; t = t.prev)
        if (t.waitStatus <= 0)
            s = t;
}
//Wake up the thread corresponding to the successor node
if (s != null)
    LockSupport.unpark(s.thread);

}

`release()` It is the top-level entry for threads to release shared resources in exclusive mode. It will release the specified amount of resources if it is completely released (i.e `state=0`),It wakes up other threads in the waiting queue to get resources.

#### 2.2.3 acquireShared(int)

This method is the top-level entry for the thread to obtain the shared resources in the shared mode. It will obtain the specified amount of resources, and return directly if the acquisition is successful. If the acquisition fails, it will enter the waiting queue until the resources are obtained. The interruption is ignored in the whole process. The following is `acquireShared()` Source code:

public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}

private void doAcquireShared(int arg) {
Final node = addwaiter (node. Shared); / / add to the end of the queue
boolean failed = true; / / success flag
try {
boolean interrupted = false; / / a flag indicating whether the waiting process has been interrupted
for (;😉 {
Final node P = node. Resolver(); / / precursor
if (p == head) {/ / if you go to the next head, because the head is the thread that gets the resources, the node is awakened. It is likely that the head uses up the resources to wake itself up
int r = tryAcquireShared(arg); / / try to get resources
If (r > = 0) {/ / success
setHeadAndPropagate(node, r); / / point the head to yourself, and there are still remaining resources to wake up later threads
p.next = null; // help GC
if (interrupted) / / if the waiting process is interrupted, the interrupt will be supplemented at this time.
selfInterrupt();
failed = false;
return;
}
}

        //Judge the state, find a safe point, enter the waiting state, and wait to be unpark() or interrupt()
        if (shouldParkAfterFailedAcquire(p, node) &&
            parkAndCheckInterrupt())
            interrupted = true;
    }
} finally {
    if (failed)
        cancelAcquire(node);
}

}

here `tryAcquireShared()`You still need to customize the synchronizer to implement it. However `AQS` The semantics of its return value has been defined: negative values represent acquisition failure;`0` A positive number indicates that the acquisition is successful, but there are no remaining resources; a positive number indicates that the acquisition is successful, there are still remaining resources, and other threads can obtain them. So here `acquireShared()` The process is:

1.  `tryAcquireShared()` Try to obtain resources, and return directly if successful;
2.  Pass if failed `doAcquireShared()` Enter the waiting queue `park()`,Until by `unpark()/interrupt()` It returns only after successfully obtaining the resource. The whole waiting process also ignores the interrupt.

`doAcquireShared(int)` This method is used to add the current thread to the rest at the end of the waiting queue. It will not return until other threads release resources to wake themselves up and successfully get the corresponding amount of resources.

#### 2.2.4 releaseShared()

`releaseShared()` It is the top-level entry for threads to release shared resources in sharing mode. It will release a specified amount of resources. If it is successfully released and allowed to wake up the waiting thread, it will wake up other threads in the waiting queue to obtain resources.

public final boolean releaseShared(int arg) {
If (tryrereleaseshared (ARG)) {/ / attempt to free resources
doReleaseShared(); / / wake up the successor node
return true;
}
return false;
}

The process of this method is also relatively simple. In a word, wake up the successor after releasing the resources. It is similar to that in exclusive mode `release()` Similar, but a little note: in exclusive mode `tryRelease()` After completely releasing resources(`state=0`)Will return only after`true` To wake up other threads, which is mainly based on the consideration of reentrant under exclusive access; And in shared mode `releaseShared()` There is no such requirement. The essence of sharing mode is to control a certain number of threads to execute concurrently. Then the thread with resources can wake up the subsequent waiting nodes when it releases some resources. For example, the total amount of resources is `13`,`A(5)`and `B(7)` Obtain resources and run concurrently,`C(4)` When I came, there was only one left `1` A resource needs to wait.`A` Release during operation `2` A resource quantity, and then `tryReleaseShared(2)` return `true` awaken `C`,`C` At first glance, only `3` One is still not enough to wait; subsequently `B`Release again `2` One,`tryReleaseShared(2)` return `true` awaken `C`,`C` At first glance `5` Enough for yourself, and then `C` You can talk to me `A` and `B` Run together. and `ReentrantReadWriteLock` Read lock `tryReleaseShared()` Only when resources are completely released(`state=0`)Just return `true`,Therefore, the custom synchronizer can be determined as needed `tryReleaseShared()`Return value of.

3\. summary
------

In this section, for `AQS` ***

*   [JUC Review-AQS Implementation principle of synchronizer](

)
*   [JDK Source code AQS Source code analysis](

)
*   [Java Concurrent AQS Explain in detail](

)


Posted by gufmn on Sun, 21 Nov 2021 01:36:00 -0800