Daily blog - CountDownLatch usage scenario analysis and source code analysis

Article catalog

Introduction to common tool classes for concurrent programming

Let's first look at several common tool classes provided in concurrent programming

  • CountDownLatch: CountDownLatch is used to block one or more current threads. Its purpose is to make these threads wait for the execution of other threads to complete. It can be simply understood as a counter. After initializing a CountDownLatch object with count=n, you need to call the CountDownLatch#countDown method of the object to devalue the counter. The thread waiting for the counter can continue to execute until the counter is 0. However, it should be noted that the thread executing the CountDownLatch#countDown method will not be blocked after the devaluation operation. The thread that really blocks the waiting event is the thread calling the CountDownLatch#await method of the CountDownLatch object. The thread will block until the counter count becomes 0.
  • CyclicBarrier: CyclicBarrier is used to block current multiple threads. Its purpose is to make these threads wait for each other. When these threads reach the barrier, they will execute down together
  • Semaphore: semaphore. You can control the number of "licenses" to ensure the cooperation between threads
  • Phaser: similar to CyclicBarrier, but the count is variable
  • Exchange: two threads exchange objects
  • Condition: you can control the "wait" and "Wake" of the thread, and the upgraded version of Object.wait()

CountDownLatch overview

CountDownLatch is located in the java.util.current package, and Java 1.5 is introduced. At the same time, several other tool classes are introduced, such as CyclicBarrier, Semaphore, concurrentashmap and BlockingQueue.

  • CountDownLatch : A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
  • CyclicBarrier : A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

CountDownLatch is a synchronization counter, which is usually used for one thread or multiple threads to wait until another group of threads completes execution.

When CountDownLatch is used, it initializes a counter. The number of counters is the number of threads. After each thread completes execution, it will call the countDown() method to count the number of counters to - 1 until the number of counters is 0, indicating that all threads have completed execution. At this time, the waiting thread calling the await() method can resume working.

Source code analysis

It can be seen that CountDownLatch is implemented based on the Sync class, and Sync inherits AQS, which is the AQS sharing mode

Usage scenario

Usage scenario 1: simulate high concurrency execution (multiple threads waiting)

For example, we plan to do 200 concurrent transactions at the same time. How can we implement it if we use code?

That's easy, brother.

I intend to simulate 200 concurrent operations. We know that CountDownLatch waits for countDown() to decrease one by one, and the thread calling await() does not work until it is 0.

Then I'll simulate making these 200 threads call await(), and then doing business together when count=0.

MMP, let me think about the scene when the company comes to the meal point and everyone rushes to dry the meal together. This CountDownLatch is the clock on the wall. Tick, tick, wait for 11:45

  • Each of us made preparations (like countDown())
  • As soon as the time comes (like await()), each of us is the business thread, and the delicious meal is the business thread

Come on, show code

package com.artisan.juc;

import java.util.concurrent.CountDownLatch;

/**
 * @author Small craftsman
 * @version 1.0
 * @description: TODO
 * @date 2021/11/5 1:42
 * @mark: show me the code , change the world
 */
public class CountDownLatchTest {


    public static void main(String[] args) throws InterruptedException {

        int cnt = 10;
        CountDownLatch countDownLatch = new CountDownLatch(cnt);

        // Simulate 10 concurrent dry meals
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    // The cooks are ready... The cooks are blocked here, waiting for orders (cnt=0)
                    countDownLatch.await();
                    System.out.println("number:" + Thread.currentThread().getName() + " Start cooking...." + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            // Simulating part of the business takes time
            Thread.sleep(10);
            // Each thread calls once, and cnt decreases by one each time until it is 0, which is the dry meal signal
            countDownLatch.countDown();
            System.out.println("countDown Execute once, number:" + (--cnt));

        }
    }

}

As you can see, we use the CountDownLatch#await() method to block and wait after multiple business threads are started. After the main thread calls the CountDownLatch#count() method to reduce the counter to 0, we let all business threads run concurrently, so as to achieve our goal of multiple threads concurrency.

Usage scenario 2: simulate the business returning to the main thread after asynchronous execution (let a thread wait)

For example, we have some business dependencies

String  str = method1();  // Time consuming 1S 
String  str2 = method2();// Time consuming 2S 

method3(str, str2); 

This mmp depends on data. Of course, you can use completable future to optimize it. We won't discuss it here

Let's see how CountDownLacth plays?

Analyze this business scenario, let method 1 and method 2 execute asynchronously, and then return to the main thread after execution to get the summary of the returned results

In our simplification, we do not get the return result

package com.artisan.juc;

import java.util.concurrent.*;

/**
 * @author Small craftsman
 * @version 1.0
 * @description: TODO
 * @date 2021/11/5 1:42
 * @mark: show me the code , change the world
 */
public class CountDownLatchTest2 {


    public static void main(String[] args) throws InterruptedException {

        // 2 is the number of threads
        CountDownLatch countDownLatch = new CountDownLatch(2);

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + " --end of execution");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();




        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + "**end of execution");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        
        countDownLatch.await();
        System.out.println("Mainline business continues");

    }


}

We can see that: add CountDownLatch#countDown() on the last line completed by each thread to make the counter - 1; When all threads complete - 1, the main thread is blocked here before countDownLatch.await(), Continue to execute your own business until the counter decreases to 0.

Posted on Mon, 08 Nov 2021 03:50:53 -0500 by towerspl