CountDownLatch, the three powerful tools of Java high concurrency programming foundation

Posted by ocpaul20 on Sun, 23 Jan 2022 00:45:22 +0100

introduction

In the previous article, we introduced the Semaphore of AQS , and it should be countdown latch , next.

What is CountDownLatch

CountDownLatch is implemented through a counter. The initial value of the counter is the number of threads. Every time a thread is executed, the value of the counter will be reduced by 1. When the value of the counter is 0 , it means that all threads have been executed, and then the threads waiting on the lock (the thread calling await method) can resume working.

Application scenario

What can countdownlatch do? What are the application scenarios? Are there any application scenarios in the actual project? This should be what we are more concerned about. Let's take a look at how the examples provided on the official website are applied https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html The official provided two demo s # and I directly converted them into pictures. By the way, I recommend the website where the code is converted into pictures https://www.dute.org/code-snapshot It's very useful.

Official website demo1

The first is a start signal that prevents any worker from proceeding until the driver is ready for them to proceed; The second is a completion signal that allows the driver to wait until all workers have completed.

"

  • The first start signal will prevent any worker from starting work before the driver arrives. To put it bluntly, the workers can't work without the driver.

  • The second is the done signal, which allows the Driver to wait until all workers finish To put it bluntly, the Driver has to wait until all the workers are finished.

Official website demo2

Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor. When all sub-parts are complete, the coordinating thread will be able to pass through await.

"

Another typical usage is to split a large task into N parts and let multiple threads (workers) execute. After each thread (Worker) executes its own part, the counter will be reduced by 1. When all sub parts are completed, the Driver will continue to execute downward. Just like the assembly line of Foxconn mobile phone processing, the assembly of one-step mobile phone needs one assembly line to cooperate with each other. A line (workers), each line does its own work. Some assembly lines are coated, some are screwed, some are quality inspected, some are charged, and some are coated. When these assembly lines are finished, a mobile phone will be assembled.

The above two are the official demo s. Here are two chestnuts we can use in our usual development:

Multiple thread waiting: simulate concurrency and let concurrent threads execute together.

Sometimes we write an interface and want to test it to see what its maximum concurrency is. Of course, we can use Jmeter , for pressure measurement, but sometimes we don't want to download tools. In fact, we can use CountDownLatch , to achieve it.

/**
 * @author: Official account: java Finance
 */
public class TestCountDownLatch1 {
     public static void main(String[] args) throws InterruptedException {
          CountDownLatch countDownLatch = new CountDownLatch(1);
          for (int i = 0; i < 5; i++) {
               new Thread(() -> {
                    try {
                         //All requests are blocked here, waiting
                         countDownLatch.await();
                         // Call test interface
                         System.out.println(Thread.currentThread().getName() + "Start execution");
                    } catch (InterruptedException e) {
                         e.printStackTrace();
                    }
               }).start();
          }
          // Get requests ready
          Thread.sleep(2000);
          // Unify all requests
          countDownLatch.countDown();
     }
}

We passed countdownlatch Await(), let multiple participant threads block and wait after starting, and then call countdownlatch. On the main thread Countdown() reduces the count to 0 and lets all threads execute down together; In this way, multiple threads execute concurrently at the same time to simulate concurrent requests.

Single thread waiting: summarize and merge after multiple threads (tasks) are completed

/**
 * @author: Official account: java Finance
 */
public class TestCountDownLatch1 {
     public static void main(String[] args) throws InterruptedException {
          int count = 3;
          CountDownLatch countDownLatch = new CountDownLatch(count);
          for (int i = 0; i < count; i++) {
               final int index = i;
               new Thread(() -> {
                    try {
                         Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000));
                         System.out.println("finish" + index + Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                         e.printStackTrace();
                    }finally{
                        countDownLatch.countDown();
                    }
               }).start();
          }
          countDownLatch.await();// The main thread is blocking. When the counter = = 0, it wakes up the main thread to execute down.
          System.out.println("Main thread:After all tasks are completed, the results are summarized");
     }
}

This scenario should be used most. For example, when we open a personal center page of e-commerce, we need to call the user information interface, user order interface, user member information and other interfaces, and then combine them to the front end. Assuming that the maximum time of each interface is 1s, if we call synchronously, the maximum time is 3s, If we use asynchronous calls and then merge the results, the maximum time-consuming is 3s. Each interface calls back the data and calls the countDown method to reduce the counter by 1. When the counter is reduced to 0, the thread will wake up the main thread and let it continue downward.

Implementation principle of CountDownLatch

CountDownLatch is a counter implemented through the state field of AQS. The initial value of the counter (state value) is the number set by new CountDownLatch. Each time countDown is called, the state value will be reduced by 1. Finally, when a thread reduces the state value to 0, it will wake up the thread calling await() to block and wait. CountDownLatch overrides the tryrereleaseshared method. Only when the state field is set to 0, that is, when tryrereleaseshared returns true, will the doReleaseShared method be executed to wake up the thread calling await.

  public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
 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;
            }
        }

Other source codes of CountDownLatch are not analyzed

summary

  • CountDownLatch , cannot reinitialize or modify the value of the CountDownLatch , internal counter.

  • CountDownLatch
    Semaphore
    AQS
    
  • CountDownLatch
    join()
    join
    
  • CountDownLatch
    n
    countDown
    
  • The implementation principle of join () is to constantly check whether the join thread is alive. If the join {thread is alive, the current thread will wait forever. Therefore, CountDownLatch is relatively flexible to use between the two.

Topics: Java Multithreading Concurrent Programming interface jmeter