CountDownLatch principle

As per the Java documentation, CountDownLatch is a synchronization utility class that allows one or more threads to wait until other threads' operations are completed. In Java concurrency , the concept of countdownlatch is a common interview question , so make sure you understand it well. In this article, I will cover the following points related to CountDownLatch in Java concurrent programming:

What is CountDownLatch

CountDownLatch was introduced in java1.5, and the concurrent utility classes introduced with it include CyclicBarrier, Semaphore, ConcurrentHashMap and BlockingQueue , which all exist under the java.util.concurrent package. The CountDownLatch class enables a thread to wait for other threads to complete their work before executing. For example, the main thread of the application may want to execute after the thread responsible for starting the framework services has started all the framework services.

CountDownLatch is implemented by a counter whose initial value is the number of threads. Every time a thread completes its task, the value of the counter is decremented by 1. When the counter value reaches 0, it indicates that all threads have completed the task, and then the thread waiting on the latch can resume executing the task.

The pseudocode for CountDownLatch looks like this:

//Main thread start
//Create CountDownLatch for N threads
//Create and start N threads
//Main thread wait on latch
//N threads completes there tasks are returns
//Main thread resume execution

How CountDownLatch works

Constructor defined in CountDownLatch.java class:

//Constructs a CountDownLatch initialized with the given count.
public void CountDownLatch(int count) {...}

The count value (count) in the constructor is actually the number of threads that the lock needs to wait for . This value can only be set once, and CountDownLatch does not provide any mechanism to reset the count value .

The first interaction with CountDownLatch is that the main thread waits for other threads. The main thread must call the CountDownLatch.await() method immediately after starting other threads . This way the main thread's operations will block on this method until other threads complete their respective tasks.

The other N threads must refer to the latch object because they need to notify the CountDownLatch object that they have completed their respective tasks. This notification mechanism is accomplished through the CountDownLatch.countDown() method; each time this method is called, the count value initialized in the constructor is decremented by 1. So when N threads all call this method, the value of count is equal to 0, and then the main thread can resume executing its own tasks through the await() method.

Usage scenarios in real-time systems

Achieving maximum parallelism : Sometimes we want to start multiple threads at the same time to achieve maximum parallelism. For example, we want to test a singleton class. If we create a CountDownLatch with an initial count of 1, and have all threads wait on this lock, then we can easily complete the test. We only need to call the countDown() method once to allow all waiting threads to resume execution at the same time.

Waiting for n threads to complete their tasks before starting execution : For example, the application startup class ensures that all N external systems are up and running before processing user requests.

Deadlock Detection: A very convenient use case is that you can use n threads to access a shared resource, the number of threads is different in each test phase, and try to generate a deadlock.

CountDownLatch usage example

In this example, I simulate an application startup class that starts n thread classes that will check the external system and notify the latch, and the launch class has been waiting on the latch. Once all external services are verified and checked, the startup class resumes execution.

BaseHealthChecker.java: This class is a Runnable that is responsible for all specific external service health checks. It removes duplicate codes and locked central control codes.

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)
    {
        super();
        this._latch = latch;
        this._serviceName = serviceName;
        this._serviceUp = false;
    }
 
    @Override
    public void run() {
        try {
            verifyService();
            _serviceUp = true;
        } catch (Throwable t) {
            t.printStackTrace(System.err);
            _serviceUp = false ;
        } finally {
            if(_latch != null) {
                _latch.countDown();
            }
        }
    }
 
    public String getServiceName() {
        return _serviceName;
    }
 
    public  boolean isServiceUp () {
         return _serviceUp;
    }
    //This methos needs to be implemented by all specific service checker
    public abstract void verifyService();
}

NetworkHealthChecker.java: This class inherits BaseHealthChecker and implements the verifyService() method. DatabaseHealthChecker.java and CacheHealthChecker.java are the same as NetworkHealthChecker.java except for service name and sleep time

public class NetworkHealthChecker extends BaseHealthChecker
{
    public NetworkHealthChecker (CountDownLatch latch)  {
        super("Network Service", latch);
    }
 
    @Override
    public void verifyService()
    {
        System.out.println("Checking " + this.getServiceName());
        try
        {
            Thread.sleep(7000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace ();
        }
        System.out.println(this.getServiceName() + " is UP");
    }
}

ApplicationStartupUtil.java: This class is the main startup class, which is responsible for initializing the lock and then waiting until all services have been instrumented.

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 ApplicationStartupUtil()
    {
    }
 
    private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();
 
    public static ApplicationStartupUtil getInstance()
    {
        return INSTANCE;
    }
 
    public static boolean checkExternalServices() 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;
    }
}

Now you can write test code to check the functionality of the latch.

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);
    }
}
Output in console:
 
Checking Network Service
Checking Cache Service
Checking Database Service
Database Service is UP
Cache Service is UP
Network Service is UP
External services validation completed !! Result was :: true

Common Interview Questions

Here are some CountDownLatch-related questions to prepare for your next interview:

  • Explain the CountDownLatch concept?
  • What is the difference between CountDownLatch and CyclicBarrier?
  • Give some examples of CountDownLatch usage?
  •  The main method in the CountDownLatch class?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325905516&siteId=291194637