When I was learning zooKeeper related knowledge points, I came across a class called CountDownLatch, which I didn’t quite understand because I hadn’t used this class before. I decided to learn about CountDownLatch in order not to interfere with later learning.

1. The concept of CountDownLatch

First, the literal translation means countdown lockout, which is easier to understand in translation. The countdown is reduced in turn, and locking, a concept often mentioned in programming, acts as a synchronous block. Is it so? Take a look at the explanation on the official website: A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. A synchronization assist that allows one or more threads to wait until a set of operations performed in another thread is complete. (the official document: docs.oracle.com/javase/7/do…). To clarify, CountDownLatch was introduced in java1.5, along with the CyclicBarrier, Semaphore, ConcurrentHashMap, and BlockingQueue concurrency utility classes. Both exist under the java.util.concurrent package. The CountDownLatch class enables a thread to wait for other threads to finish their work before executing. For example, the main thread of an application wants to execute after the thread responsible for starting the framework service has started all the framework services. CountDownLatch is implemented through a counter whose initial value is the number of threads. Each time a thread completes its task, the counter is reduced by one. When the counter value reaches 0, it indicates that all threads have completed the task, and the threads waiting on the lock can resume the task. To summarize, CountDownLatch is a synchronization utility class under the java.util.concurrent package that allows one or more threads to wait until a set of operations are performed in another thread.

2. CountDownLatch

You need to use CountDownLatch’s constructor, which is defined in the CountDownLatch. Java class: The count in the constructor is actually the number of threads that need to wait for the latch. This value can only be set once, and CountDownLatch does not provide any mechanism for resetting the value. The first interaction with CountDownLatch is to call the countdownlatch.await () method. This will cause the current thread calling the method to block on the method until the CountDownLatch. CountDown () method is called by other threads, and the blocked thread is released until the CountDownLatch constructor is decrement to zero. Of course, other threads must reference the same latch object because they need to notify the CountDownLatch object that they have completed their task. This notification mechanism is done through the countdownlatch.countdown () method; Each time this method is called, the value of count initialized in the constructor decreases by one. So when N threads have called this method and count equals zero, the blocking thread can resume its task with await() method.

3. Usage scenarios of CountDownLatch

3.1 Achieve maximum parallelism

Note that parallelism, not concurrency, emphasizes that multiple threads start executing at the same time. Similar to a race, multiple threads are placed at the starting point, wait for the starting gun to go off, and then run at the same time. This is done by initializing a shared CountDownLatch(1) and initializing its calculator to 1. Multiple threads first countdownlatch.await() before starting the task. When the main thread calls countDown(), the counter becomes 0 and multiple threads are awakened at the same time.

3.2 Wait for n threads to complete their tasks before starting execution

A thread waits for n threads to complete before it starts running. Initialize CountDownLatch’s counter to new CountDownLatch(n). When a task thread completes, the counter is reduced by 1. Countdownlatch.countdown (). The thread awaiting () on CountDownLatch is awakened. A typical application scenario is that when starting a service, the main thread waits for multiple components to load before resuming execution.

4. DOS and don ‘ts for CountDownLatch

CountDownLatch is disposable. The value of the calculator can only be initialized once in the constructor, and there is no mechanism to set the value again. When CountDownLatch is used, it cannot be used again.

5. Examples of using CountDownLatch

Example 1:

public class CountDownLatchTest {

    public static void main(String[] args) {
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(2);

        for (int i=0; i<2; i++){
            Thread thread = new Thread(new Player(begin,end));
            thread.start();
        }

        try {
            System.out.println("the race begin");
            begin.countDown();
            end.await();
            System.out.println("the race end");
        } catch(Exception e){ e.printStackTrace(); }}}/** ** player */
class Player implements Runnable{

    private CountDownLatch begin;

    private CountDownLatch end;

    Player(CountDownLatch begin,CountDownLatch end){
        this.begin = begin;
        this.end = end;
    }

    public void run(a) {
        try {
            begin.await();
            System.out.println(Thread.currentThread().getName() + " arrived !");;
            end.countDown();
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The running results are as follows:For ease of understanding, it is recommended to comment out one of begin.await(), begin.countdown (), end.await(), end.countdown () so that it is easy to understand.

Example 2:

In this example, I simulated an application startup class that started with n thread classes that would check the external system and notify the lockout, and the startup class was waiting on the lockout. Once all external services have been validated and checked, start class recovery execution.

Basehealthchecker.java: This class is a Runnable that is responsible for checking the health of all specific external services. It removes duplicate code and locked-in central control code.

public abstract class BaseHealthChecker implements Runnable {

    private CountDownLatch _latch;
    private String _serviceName;
    private boolean _serviceUp;

    // Get latch object in constructor so that after completing the task, thread can countDown() the latch
    public BaseHealthChecker(String serviceName, CountDownLatch latch) {
        this._latch = latch;
        this._serviceName = serviceName;
        this._serviceUp = false;
    }

    @Override
    public void run(a) {
        try {
            verifyService();
            _serviceUp = true;
        } catch (Throwable t) {
            t.printStackTrace(System.err);
            _serviceUp = false;
        } finally {
            if(_latch ! =null) { _latch.countDown(); }}}public String getServiceName(a) {
        return _serviceName;
    }

    public boolean isServiceUp(a) {
        return _serviceUp;
    }

    public abstract void verifyService(a);
}
Copy the code

NetworkHealthChecker. Java: this class inherits BaseHealthChecker, implements the verifyService () method. DatabaseHealthChecker. Java and CacheHealthChecker. Java besides the service name and sleep time, and NetworkHealthChecker. Java is the same.

public class NetworkHealthChecker extends BaseHealthChecker {

    public NetworkHealthChecker (CountDownLatch latch)  {
        super("Network Service", latch);
    }

    @Override
    public void verifyService(a) {
        System.out.println("Checking " + this.getServiceName() + "...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.getServiceName() + " is UP"); }}Copy the code

ApplicationStartupUtil. Java: this class is a main start class, it is responsible for the initialization atresia, then wait, until all services are finished testing.

public class ApplicationStartupUtil {
    //List of service checkers
    private static List<BaseHealthChecker> _services;

    //This latch will be used to wait on
    private static CountDownLatch _latch;

    private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();

    public static ApplicationStartupUtil getInstance(a) {
        return INSTANCE;
    }

    public static boolean checkExternalServices(a) throws Exception {
        //Initialize the latch with number of service checkers
        _latch = new CountDownLatch(3);

        //All add checker in lists
        _services = new ArrayList<BaseHealthChecker>();
        _services.add(new NetworkHealthChecker(_latch));
        _services.add(new CacheHealthChecker(_latch));
        _services.add(new DatabaseHealthChecker(_latch));

        //Start service checkers using executor framework
        Executor executor = Executors.newFixedThreadPool(_services.size());
        for (final BaseHealthChecker v : _services) {
            executor.execute(v);
        }

        //Now wait till all services are checked
        _latch.await();

        //Services are file and now proceed startup
        for(final BaseHealthChecker v : _services) {
            if(! v.isServiceUp()) {return false; }}return true; }}Copy the code

Now you can write test code to test the locking function.

public class Main {

    public static void main(String[] args) {
        boolean result = false;
        try {
            result = ApplicationStartupUtil.checkExternalServices();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("External services validation completed !! Result was :: "+ result); }}Copy the code

Reference:

www.cnblogs.com/cuglkb/p/85…

www.cnblogs.com/catkins/p/6…

www.cnblogs.com/Lee_xy_z/p/…