1. Introduction

In daily development, it is common to encounter scenarios where multiple threads need to be started in the main thread to execute tasks in parallel, and the main thread needs to wait for all subthreads to complete execution before summarizing.

Before CountDownLatch appeared, the join() method of Thread was used to do this. However, the join() method was not flexible enough to meet the needs of different scenarios, so the JDK development team provided CountDownLatch with this class:

public static void CountDownLatchUsed() throws InterruptedException { CountDownLatch downLatch = new CountDownLatch(6); for (int i = 0; i < 6; I++) {new Thread(() -> {system.out.println (thread.currentthread ().getname () + "\t "); downLatch.countDown(); }, String.valueOf(i)).start(); } downLatch.await(); System.out.println(thread.currentThread ().getName() + ""); } -- Output result 0 self study leave 5 self study leave 4 self study leave 2 self study leave 3 self study leave 1 self study leaveCopy the code

2. Implementation principle

You can guess from the name of CountDownLatch that it is a counter inside, and that this counter is decrement. Let’s take a look at the source code to see when the JDK development team initializes the counter, when it decrements the counter, what it does when the counter goes to zero, and how multiple threads synchronize with the timer. To see the internal structure of CountDownLatch, let’s first look at the class diagram:

As you can see from the class diagram, CountDownLatch is implemented using AQS. By using the constructor below, you can see that you are actually assigning the counter to AQS ‘state variable, which is used here to represent the counter value.

/** * Constructs a {@code CountDownLatch} initialized with the given count. * * @param count the number of times {@link #countDown} must be invoked * before threads can pass through {@link #await} * @throws IllegalArgumentException if {@code count} is negative */ public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count The < 0 "); this.sync = new Sync(count); }Copy the code

Let’s look at some of the important methods in CountDownLatch and see how they invoke AQS to implement functionality.

2.1 Void await() method

#CountDownLatch/await

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
Copy the code

#AQS/acquireSharedInterruptibly

Public final void acquireSharedInterruptibly (int arg) throws InterruptedException {/ / how to thread is terminal, If (thread.interrupted ()) throw new InterruptedException(); if (thread.interrupted ()) throw new InterruptedException(); / / check to see if the current value of the counter to 0, 0 is returned directly, otherwise enter AQS queue waiting for the if (tryAcquireShared (arg) < 0) / / join synchronous queue doAcquireSharedInterruptibly (arg); }Copy the code

The Sync class implements the AQS interface

protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1:1; }Copy the code

As can be seen from the above code, the characteristics of this method are that the thread can be interrupted when the resource is acquired, and the resource is shared when the resource is acquired. AcquireSharedInterruptibly first determines whether the current thread has been interrupted, if an exception is thrown, otherwise call the sync tryAcquireShared methods achieve see if current state value is 0, is the current thread await () method returns directly, Otherwise call AQS doAcquireSharedInterruptibly method for the current thread is blocked.

In addition, we can see that the arG parameter passed by tryAcquireShared is not used. We call tryAcquireShared only to check whether the current state value is 0, and we do not call CAS to subtract 1 from the current state value.

2.2 The void countDown() method

#CountDownLatch/countDown

/**
 * Decrements the count of the latch, releasing all waiting threads if
 * the count reaches zero.
 *
 * <p>If the current count is greater than zero then it is decremented.
 * If the new count is zero then all waiting threads are re-enabled for
 * thread scheduling purposes.
 *
 * <p>If the current count equals zero then nothing happens.
 */
public void countDown() {
    sync.releaseShared(1);
}
Copy the code

#sync/releaseShared

Public final Boolean releaseShared(int arg) {// sync implementation if (tryReleaseShared(arg)) {// AQS doReleaseShared(); return true; } return false; }Copy the code

#CountDownLatch/Sync/tryReleaseShared

protected boolean tryReleaseShared(int releases) { // Decrement count; Signal when transition to zero // Loop through CAS until the current thread successfully completes CAS { int c = getState(); If (c == 0) return false; if (c == 0) return false; Int nextc = c-1; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; }}Copy the code

3. Summary

CountDownLatch is implemented using AQS. Use the AQS state variable to store the counter value. You first set the status value (the value of the counter) when you initialize CountDownLatch. When multiple threads call the Countdown method, they actually decrement the AQS status value by themselves. When a thread calls the await method, the current thread will be placed on the AQS blocking queue until the counter reaches 0.

Other threads call the CountDownLatch method to decrement the counter value by 1. When the counter value goes to 0, the current thread also calls the AQS doReleaseShared method to activate the thread that has been blocked by calling the await() method.