This is the sixth day of my participation in Gwen Challenge

1. CountDownLatch profile

In the previous article, we introduced the CyclicBarrier class, which allows you to perform batch summaries. This time, CountDownLatch is a “countdown” function.

2. Simple application of CountDownLatch

The CountDownLatch source code is concise and provides two typical methods for implementing the “countdown function.” CountDownLatch specifies the number of latches in the constructor. When the number of latches is zero, all threads in the wait state are awakened. It provides the following key approaches:

  • countDown(): by callingcountDownMethod to realize latch lock- 1The effect.
  • await(): makes the current thread wait.
  • getCount(): Gets the number of latches.

Let’s take a look at a sample code:

public class MyTest {
    static CountDownLatch countDownLatch;
    public static void main(String[]args){
        countDownLatch = new CountDownLatch(3);
        for(int i=0; i<3; i++){new Thread(new Runnable() {

                @Override
                public void run(a) {
                    System.out.println(Thread.currentThread().getName() + "-- to the door");
                    System.out.println(Thread.currentThread().getName() + "-- boarded");
                    countDownLatch.countDown();
                }
            }).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-- Boarding complete."); }}Copy the code

In the example code, the constructor creates a CountDownLatch method with 3 latches, creates three threads, calls the countDown() method inside each thread once, and finally calls the await method in the main thread to block. Finish the output. Execution Result:

Thread-0-- Reaching the door Thread-1-- Reaching the door Thread-0-- Boarded Thread-1-- Boarded Thread-2-- Reaching the door Thread-2-- Boarding is completeCopy the code

CountDownLatch has the following features:

  1. Thread executioncountDownMethod can continue to execute subsequent code, the thread does not block waiting;
  2. latchThe number of0Will immediately wake upawaitWaiting threads;

3. CountDownLatch source code analysis

Considering the description of the CountDownLatch function above, if we were to implement such a function, our first thought would be to maintain a Count Count variable in the utility class, and then maintain the judgment on that variable. So let’s look at how CountDownLatch is implemented.

public class CountDownLatch {
    /** * Synchronization control For CountDownLatch. * Uses AQS state to represent count. */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount(a) {
            return getState();
        }

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

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0; }}}private final Sync sync;

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    /** * Makes the current thread wait until the number of locks is 0, or the current thread interrupt * returns */ immediately if the current latch number is 0
    public void await(a) throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    /** * Reduce the number of latch locks and release all waiting threads when the number of LATCH locks is zero. * /
    public void countDown(a) {
        sync.releaseShared(1);
    }

    public long getCount(a) {
        return sync.getCount();
    }

    public String toString(a) {
        return super.toString() + "[Count = " + sync.getCount() + "]"; }}Copy the code

First, in terms of constructors, CountDownLatch provides the argument constructor:

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
Copy the code

The Sync class here is an internal class that integrates the AQS implementation within CountDownLatch.

Sync(int count) {
    setState(count);
}
Copy the code

In the Sync constructor, the AQS setState method is called to change the state value to the count value.

As described above, the CountDownLatch and await methods are the most used for the CountDownLatch class.

3.1 countDown () method

public void countDown(a) {
    sync.releaseShared(1);
}

/** The method defined in the AQS class */
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

Copy the code

Inside the countDown method is a call to Sync’s releaseShared method to try to release the public lock state.

protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0; }}Copy the code

If the state value is 0, false is returned. If the CAS operation succeeds, nexTC == 0 is returned. When true is finally returned by consulting (state == 0), the doReleaseShared() method in releaseShared is executed, The blocked threads are awakened to start executing by the unparksucceeded () method inside the doReleaseShared() method.

3.2 await () method

public void await(a) throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

/***AQS */
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

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

Here call acquireSharedInterruptibly lock () method of the application, if the lock is held the state! = 0, then through doAcquireSharedInterruptibly () to lock application.

4. To summarize

CountDownLatch is commonly used in the following scenarios:

  • Ensure that a computation does not proceed until all the resources it needs have been initialized.
  • Ensure that a service is started only after all other services on which it depends have been started.
  • Wait until the owners of an operation are ready to proceed.