Learning Java concurrency tools -- CountDownLatch and CyclicBarrier

Keywords: Java Back-end

preface

Continue to briefly summarize the tool classes for concurrent process control in Java. This article summarizes CountDownLatch and CyclicBarrier

CountDownLatch

Latch means door latch in Chinese. CountDown means CountDown. Count down to 0 and the door latch is opened. You can see the usage scenario of this tool from the name. For example, in a lot of shopping, five people form a group. They are not eligible to buy until they form a group.

There are usually two common uses for CountDownLatch

1. One thread waits for the other threads to count down to 0 before proceeding to the next step


The image above shows a method of using CountDownLatch. The Ta thread in the figure can continue to the next step only after other threads count down to 0.
Simple code example: simulating a product requires 5 quality inspectors to complete quality inspection before it can be released

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:Simulated factory quality inspection
 * A product release requires 5 people to complete quality inspection
 */
@Slf4j
public class CountDownLatchDemo01 {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(5);//Five people are required to complete the quality inspection
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int number = i+1;
            Runnable runnable=new Runnable(){
                @Override
                public void run() {
                    try {
                        //Time to simulate quality inspection
                        long checkTime = (long) (Math.random() * 10000);
                        Thread.sleep(checkTime);
                        System.out.println("No."+number+"Completed quality inspection, time consuming:"+checkTime+"ms");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //When a thread completes quality inspection, it calls the countDown method until the value of CountDownLatch is 0, and the main thread can execute
                        countDownLatch.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }
        System.out.println("Wait for 5 quality inspectors to complete the inspection");
        //The main thread waits for 5 sub threads to complete quality inspection and calls the await method of CountDownLatch
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Everyone has finished the work and the product can be released");
        //Stop thread pool
        //Judge whether the thread pool is stopped
        executorService.shutdown();
        while(!executorService.isTerminated()){
            //If the thread pool does not complete the task, the main thread will idle until the thread pool runs
        }
    }
}

2. Multiple threads wait for a thread to count down to 0 before proceeding to the next step.

Here, a more complex comprehensive example is used to illustrate. As shown in the following code, a simple example of sports long-distance running is simulated, and two CountDownLatch objects are used. The relevant code explanation is in the comments

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:Athletes running in simulated games
 * Multiple athletes wait for a unified command to start
 * At the same time, all athletes can't finish the game until the end
 */
@Slf4j
public class CountDownLatchDemo02 {

    public static void main(String[] args) throws InterruptedException {
        //Initialize the started CountDownLatch. The specified number is 1
        CountDownLatch beginCountDownLatch = new CountDownLatch(1);
        //The specified number of CountDownLatch after initialization is 5
        CountDownLatch endCountDownLatch = new CountDownLatch(5);
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("No."+no+"Ready, wait for the starting gun to start");
                    try {
                        beginCountDownLatch.await();//The child thread will block here and wait for the main thread to issue instructions
                        System.out.println("No."+no+"Start running");
                        long runTime = (long) (Math.random() * 10000);
                        Thread.sleep(runTime);//Simulate the random running duration. After execution, it means running to the end
                        System.out.println("No."+no+"Time to reach the destination:"+runTime+"ms");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //Reach the end and send a signal
                        endCountDownLatch.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }
        //The main thread is actually a referee, simulating referee preparation
        Thread.sleep(5000);
        System.out.println("The starting gun rang and all the athletes began to run");
        beginCountDownLatch.countDown();
        endCountDownLatch.await();//The main thread is blocked and the referee continues to wait for all athletes to reach the finish line.
        System.out.println("Everyone is at the end. The game is over");
        //Judge whether the thread pool is stopped
        executorService.shutdown();
        while(!executorService.isTerminated()){
            //If the thread pool does not complete the task, the main thread will idle until the thread pool runs
        }
    }
}

In the above example, it is the same as in the first scenario that the product can be released only after five quality inspectors have completed quality inspection.

Based on the above two common usage scenarios, CountDownLatch also supports one group of threads waiting for another group of threads to execute.

It should be noted that CountDownLatch cannot be reused. If you want to re count, you can consider using CyclicBarrier or re creating CountDownLatch.

CyclicBarrier

Cyclic means loop in Chinese, and Barrier means fence. Circular fence indicates that this is reusable. This is actually very similar to CountDownLatch in actual use scenarios.

Go straight to the example

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:Example of circular fence
 * Compared with CountDownLatch, CyclicBarrier is reusable
 */
@Slf4j
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        //Instantiate a CyclicBarrier. The second parameter is the operation of the CyclicBarrier after all threads arrive
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("Everyone is here. Let's go together");
            }
        });

        for (int i = 0; i < 10; i++) {
            //Start each thread
            new Thread(new Task(i, cyclicBarrier)).start();
        }
    }

    static class Task implements Runnable {

        private int id;
        private CyclicBarrier cyclicBarrier;

        public Task(int id, CyclicBarrier cyclicBarrier) {
            this.id = id;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("thread " + id + "Now go to the assembly site");
            try {
                Thread.sleep((long) (Math.random() * 10000));
                System.out.println("thread " + id + "When we get to the assembly site, we start waiting for other threads to arrive");
                cyclicBarrier.await();//Wait for other threads to arrive.
                System.out.println("thread " + id + "set out");//When all threads are in place, they will execute the code together
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

Operation results

The difference between the two

The usage scenarios of the two are very similar, but there are still some differences
1. The two functions are different. CyclicBarrier cannot continue to execute until a fixed number of threads reach the specified location (CountDownLatch seems to have this effect), but CountDownLatch only needs to wait until the set value is 0. After all, countdown can be performed multiple times in a thread. CountDownLatch focuses on events, while CyclicBarrier focuses on threads.
2. Reusability is different. When CountDownLatch counts down to 0, it cannot be used again unless a new instance is created and the CyclicBarrier is reusable. In the above CyclicBarrier instance, although the CyclicBarrier initialization value is 5, the cycle starts 10 threads and can still be reused.

summary

After a brief review of their usage scenarios, we will begin to summarize the contents of AQS

Posted by webAmeteur on Sun, 28 Nov 2021 13:16:17 -0800