CountDownLatch thread synchronization for Java Concurrent Programming learning

Posted by davidjwest on Thu, 10 Feb 2022 08:00:51 +0100

preface

  • Scene description

    Briefly describe this step. Open three threads and call three different methods. Use the countdowncatch counter to wait for the completion of all three methods, and then merge the data

Basic concepts

  • introduce

    CountDownLatch is a synchronization tool class that allows one or more threads to wait until other threads finish executing.

  • principle

    CountDownLatch is implemented through a counter whose initialization value is the number of threads. Each time a thread completes its task, the value of the counter will be reduced by 1. When the counter reaches 0, it indicates that all threads have completed the task, and then the thread waiting on the lock can resume executing the task.

    Pseudo code

      Main thread start
      Create CountDownLatch for N threads
      Create and start N threads
      Main thead wait on latch
      N threads completes there tasks are returns
      Main thread resume execution
    
    
    
  • Applicable scenario

    1. A thread waits for n threads to finish executing before starting to run. Initialize the counter of CountDownLatch to new CountDownLatch(n). Whenever a task thread finishes executing, the counter will be decremented by 1 CountDownLatch Countdown(), when the counter value becomes 0, the thread of await() on CountDownLatch will be awakened. A typical application scenario is that when starting a service, the main thread needs to wait for multiple components to load before continuing to execute.

    2. Realize the maximum parallelism of multiple threads starting to execute tasks. Note that parallelism, not concurrency, emphasizes that multiple threads start executing at the same time. Similar to a race, put multiple threads at the starting point, wait for the starting gun to sound, and then start running at the same time. The method is to initialize a shared CountDownLatch(1) and initialize its calculator to 1. Multiple threads first countdownlatch before starting to execute tasks Await(), when the main thread calls countDown(), the counter changes to 0 and multiple threads are awakened at the same time.

  • instructions

      //The thread calling the await() method is suspended and waits until the count value is 0
      public void await() throws InterruptedException { };
    
      //Similar to await(), it will continue to execute if the count value has not changed to 0 after waiting for a certain time
      public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
    
      //Decrement count by 1
      public void countDown() { };  
    
    
    
    

Implementation process

  • Function description

    Simulate an application startup class, start N threads at the beginning, check whether N external services are normal and notify locking; The startup class has been waiting on the lock. Once all external services are verified and checked, the execution of the startup class will be resumed.

  • Implementation logic

    1. The BaseHealthChecker abstract base class is a base class that implements the Runnable interface and is responsible for the health check of all specific external services.

    2. NetworkHealthChecker.java,DatabaseHealthChecker.java and cachehealthchecker Java inherits from BaseHealthChecker and references CountDownLatch instance. Except for different service name and sleep time, they all implement their own verifyService method.

    3. ApplicationStartupUtil.java: is a main startup class. It is responsible for initializing locking, and then waiting for all services to be checked before resuming execution.

Case code

  • BaseHealthChecker abstract base class

      package link.lycreate.springbooteasyexceldemo.thread;
    
      import java.util.concurrent.CountDownLatch;
    
      /**
      * @ClassName BaseHealthChecker
      * @Description External service health check base class$
      * @Author charlesYan
      * @Date 2021/5/13 18:13
      * @Version 1.0
      **/
      public abstract class BaseHealthChecker implements Runnable {
    
          private CountDownLatch _latch;
          private String _serviceName;
          private boolean _serviceUp;
    
          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;
          }
    
          public abstract void verifyService();
      }
    
    
    
    
    
    
  • Abstract base class subclass NetworkHealthChecker

    
      package link.lycreate.springbooteasyexceldemo.thread;
    
      import lombok.extern.slf4j.Slf4j;
    
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.TimeUnit;
    
      /**
      * @ClassName NetworkHealthChecker
      * @Description TODO Network service health check$
      * @Author charlesYan
      * @Date 2021/5/14 9:09
      * @Version 1.0
      **/
      @Slf4j
      public class NetworkHealthChecker extends BaseHealthChecker {
    
          public NetworkHealthChecker(CountDownLatch latch) {
              super("Network Service", latch);
          }
    
          @Override
          public void verifyService() {
              log.info("Checking {}",this.getServiceName());
              try {
                  TimeUnit.SECONDS.sleep(7);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              log.info("{} is UP",this.getServiceName());
          }
      }
    
    
    
    
    
  • Abstract base class subclass DatabaseHealthChecker

    
      package link.lycreate.springbooteasyexceldemo.thread;
    
      import lombok.extern.slf4j.Slf4j;
    
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.TimeUnit;
    
      /**
      * @ClassName DatabaseHealthChecker
      * @Description Database service health check class$
      * @Author charlesYan
      * @Date 2021/5/14 9:35
      * @Version 1.0
      **/
      @Slf4j
      public class DatabaseHealthChecker extends BaseHealthChecker {
    
          public DatabaseHealthChecker(CountDownLatch latch) {
              super("Database Service", latch);
          }
    
          @Override
          public void verifyService() {
              log.info("Checking {}",this.getServiceName());
              try {
                  TimeUnit.SECONDS.sleep(3);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              log.info("{} is UP",this.getServiceName());
          }
      }
    
    
    
    
    
  • Abstract base class subclass CacheHealthChecker

      package link.lycreate.springbooteasyexceldemo.thread;
    
      import lombok.extern.slf4j.Slf4j;
    
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.TimeUnit;
    
      /**
      * @ClassName CacheHealthChecker
      * @Description TODO Cache service health check class$
      * @Author charlesYan
      * @Date 2021/5/14 11:26
      * @Version 1.0
      **/
      @Slf4j
      public class CacheHealthChecker extends BaseHealthChecker {
    
          public CacheHealthChecker(CountDownLatch latch) {
              super("Cache Service", latch);
          }
    
          @Override
          public void verifyService() {
    
              log.info("Checking {}",this.getServiceName());
              try {
                  TimeUnit.SECONDS.sleep(3);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              log.info("{} is UP",this.getServiceName());
    
          }
      }
    
    
    
    
    
  • ApplicationStartupUtil main startup class

    
      package link.lycreate.springbooteasyexceldemo.thread;
    
      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.Executor;
      import java.util.concurrent.Executors;
    
      /**
      * @ClassName ApplicationStartupUtil
      * @Description Main startup class$
      * @Author charlesYan
      * @Date 2021/5/14 11:38
      * @Version 1.0
      **/
      public class ApplicationStartupUtil {
    
          private static List<BaseHealthChecker> _services;
          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
          {
              _latch = new CountDownLatch(3);
              _services = new ArrayList<BaseHealthChecker>();
              _services.add(new NetworkHealthChecker(_latch));
              _services.add(new CacheHealthChecker(_latch));
              _services.add(new DatabaseHealthChecker(_latch));
    
              Executor executor = Executors.newFixedThreadPool(_services.size());
    
              for(final BaseHealthChecker v : _services)
              {
                  executor.execute(v);
              }
    
              _latch.await();
    
              for(final BaseHealthChecker v : _services)
              {
                  if( ! v.isServiceUp())
                  {
                      return false;
                  }
              }
              return true;
          }
      }
    
    
    
    
    
    
    
  • main method test class

      package link.lycreate.springbooteasyexceldemo;
    
      import link.lycreate.springbooteasyexceldemo.thread.ApplicationStartupUtil;
      import lombok.extern.slf4j.Slf4j;
    
      /**
      * @ClassName MainTest
      * @Description main Method test class$
      * @Author charlesYan
      * @Date 2021/5/14 11:49
      * @Version 1.0
      **/
      @Slf4j
      public class MainTest {
    
    
          public static void main(String[] args)
          {
              boolean result = false;
              try {
                  result = ApplicationStartupUtil.checkExternalServices();
              } catch (Exception e) {
                  e.printStackTrace();
              }
              log.info("External services validation completed !! Result was :: {}",result);
          }
      }
    
    
    
    

Output log

```log

  [pool-1-thread-2] INFO link.lycreate.springbooteasyexceldemo.thread.CacheHealthChecker - Checking Cache Service
  [pool-1-thread-3] INFO link.lycreate.springbooteasyexceldemo.thread.DatabaseHealthChecker - Checking Database Service
  [pool-1-thread-1] INFO link.lycreate.springbooteasyexceldemo.thread.NetworkHealthChecker - Checking Network Service
  [pool-1-thread-3] INFO link.lycreate.springbooteasyexceldemo.thread.DatabaseHealthChecker - Database Service is UP
  [pool-1-thread-2] INFO link.lycreate.springbooteasyexceldemo.thread.CacheHealthChecker - Cache Service is UP
  [pool-1-thread-1] INFO link.lycreate.springbooteasyexceldemo.thread.NetworkHealthChecker - Network Service is UP
  [main] INFO link.lycreate.springbooteasyexceldemo.MainTest - External services validation completed !! Result was :: true


```

summary

  • CyclicBarrier

    CyclicBarrier adds to the specified threshold, while CountDownLatch subtracts to 0. The two are just the opposite

  • CountDownLatch

    CountDownLatch is one-time. The value of the calculator can only be initialized once in the construction method. After that, there is no mechanism to set the value again. When CountDownLatch is used, it cannot be used again

  • matters needing attention

    
      try{ 
        //Execute code first 
        do.... 
        //last
        countDown .countDown(); 
      }catch(Exception ex){ 
        .countDown();//In case of any abnormality, the one above countDown() will not execute. Add this line of code to prevent thread pool OOM 
      }
    
    
    
    
    

Reference link

  • Understanding and use of CountDownLatch

    https://www.cnblogs.com/Lee_xy_z/p/10470181.html

  • CountDownLatch stepped on the pit

    https://blog.csdn.net/u010598327/article/details/82083038

  • CountDownLatch

    https://www.jianshu.com/p/4b6fbdf5a08f

  • countDownLatch

    https://www.jianshu.com/p/e233bb37d2e6

  • Java concurrency – CountDownLatch Example

    https://howtodoinjava.com/java/multi-threading/when-to-use-countdownlatch-java-concurrency-example-tutorial/

Topics: Design Pattern Multithreading JUC