In previous articles, we learned how AQS and ReentrantLock are implemented. In this article we introduce CountDownLatch, another synchronization tool in Java.
CountDownLatch is also a common synchronization tool, which is also based on AQS.
1 Basic Usage
CountDownLatch CountDownLatch is an important synchronization tool in Java. It is used when we want the main thread to wait for the logic of multiple sliver threads to complete. The basic use of this tool is as follows:
/**
* Create By IntelliJ IDEA
*
* @author: XieHua
* @date: 2021-06-07 * / to him
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i ++) {
Thread thread = new Thread(new MyRunnable(countDownLatch), "THREAD" + i);
thread.start();
}
countDownLatch.await();
}
public static class MyRunnable implements Runnable {
private CountDownLatch countDownLatch;
public MyRunnable (CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public void run(a) { String threadName = Thread.currentThread().getName(); System.out.println(threadName); countDownLatch.countDown(); }}}Copy the code
In this code, we create a CountDownLatch object of 5, and then block the main thread by each of the five child threads calling countdownlatch.countdown () and countdownlatch.await (). When we run the program, we find that the execution of five child threads will end, and the result is as follows:
2 class structure
The CountDownLatch class structure is relatively simple, with an inner class of Sync. Synck inherits from AQS, and CountDownLatch is implemented by this class. Its class diagram is as follows:
3 Implementation Principle
From our initial example, we know that the main methods for CountDownLatch are
- The constructor
countDown
await
Let’s take a look at the logic of each of these methods
3.1 Constructors
The constructor source is as follows:
public CountDownLatch(int count) {
// Count < 0 throws an exception
if (count < 0) throw new IllegalArgumentException("count < 0");
// Create Sync object
this.sync = new Sync(count);
}
Sync(int count) {
// Set the state field to count
setState(count);
}
Copy the code
There is no logic in the constructor, just setting the value of state. In this method, we find that the state field in the AQS is used in CountDownLatch to record the number of threads waiting.
3.2 await
This method blocks the main thread, and its source is as follows:
public void await(a) throws InterruptedException {
// Call the method in AQS, where the tryAcquireShared method is called
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// The thread was interrupted and raised an exception
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared is also a template method that needs to be implemented using a subclass
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// the implementation of countdownlatch.sync
protected int tryAcquireShared(int acquires) {
// state = 0 indicates success
return (getState() == 0)?1 : -1;
}
Copy the code
Through the above source code, we found that CountDonwLatch AQS is used in sharing mode, because in AQS source is introduced in the article we didn’t say sharing pattern code, he simple point in to look at here, doAcquiresSharedInterruptibly source code is as follows:
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
// Insert nodes in shared mode into the blocking queue
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
/ / spin
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// Try to obtain the shared lock
int r = tryAcquireShared(arg);
// The value greater than 0 is successful
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return; }}// Changes the state value of the front node to -1 and blocks the thread by calling the locksupport. park method
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw newInterruptedException(); }}finally {
if(failed) cancelAcquire(node); }}Copy the code
This section of method logic is also relatively simple, its process is as follows:
1. Try to acquire the lock
2. Failed to acquire the lock, initialize the blocking queue and insert the thread into the queue in shared mode
3. Spin mode changes the leading node state to -1 and blocks the current thread
3.3 countDown method
This method is used to reduce the count value, when the value is reduced to 0, the lock is released successfully, wake up the main thread, the source code is as follows:
public void countDown(a) {
// Call the AQS method to release the shared lock. In releaseShared the tryReaseShared method is called
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// Template methods need to be implemented in CountDownLatch
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
/ / spin
for (;;) {
// Current count
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
The CAS operation modifies the value of state
if (compareAndSetState(c, nextc))
// If the value is 0, the release is successful
return nextc == 0; }}private void doReleaseShared(a) {
/ / spin
for (;;) {
Node h = head;
if(h ! =null&& h ! = tail) {int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if(! compareAndSetWaitStatus(h, Node.SIGNAL,0))
continue; // loop to recheck cases
// Wake up the next node locksupport.unpark
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break; }}Copy the code
4 summarizes
So far, the principle of CountDownLatch has been introduced and the code logic is relatively simple. After understanding the processing logic of AQS, the functions realized on the basis of this latch will be easily understood.
Use the state in the AQS in CountDownLatch to record the number of threads to wait, call the await method to add the main thread to the AQS blocking queue, call the countDown method to reduce the value of state, and wake up the main thread when the state drops to 0.
If you feel helpful, welcome to pay attention to the public number, your attention is my biggest motivation to update ~