53-How does CountDownLatch arrange the thread execution order?

Let's first introduce CountDownLatch, which is a tool class for concurrent process control provided by the JDK. It is under the java.util.concurrent package and added after JDK1.5. Let's give an example to illustrate in what scenarios it is mainly used.

For example, when we go to the amusement park to ride the rapids, sometimes there are not so many people in the amusement park. At this time, the administrator will ask you to wait for a while and wait until the people are full before sailing. In this way, you can save money to a certain extent. The cost of an amusement park. There are as many people as there are seats. This is the core idea of ​​CountDownLatch. You can only start after a set value is reached.

flow chart

Let’s express the example of torrential advancement in a flowchart:
Insert picture description here
you can see that the initial value of CountDownLatch is set to 3 at the beginning, and then the T0 thread calls the await method when it comes up. Its function is to let this thread start waiting and wait for later T1, T2, and T3, each time they call the countDown method, the value of 3 will be reduced by 1, that is, from 3 to 2, from 2 to 1, from 1 to 0, once it is reduced to 0, this T0 It is equivalent to reaching the conditions for triggering to continue running, so it resumes running.

Main method introduction

Here are the main methods of CountDownLatch.

(1) Constructor: public CountDownLatch(int count) {};

Its constructor is to pass in a parameter, the parameter count is the value that needs to be counted down.

**(2) await(): **The thread that called the await() method starts to wait and will not continue execution until the countdown ends, that is, when the count value is 0.

(3) await(long timeout, TimeUnit unit): await() has an overloaded method, which will pass in timeout parameters. This method is similar to await(), but the timeout period can be set here. Wait again.

**(4) countDown():** Count down the value by 1, that is, subtract 1 from the count value until it is reduced to 0, the thread that was waiting before will be called up.

usage

Next, let's introduce two typical usages of CountDownLatch.

Usage 1: One thread waits for the completion of multiple other threads before continuing its work

In actual scenarios, in many cases we need to initialize a series of preconditions (such as establishing a connection and preparing data). Before these preconditions are completed, the next step cannot be performed, so this is a very important way to use CountDownLatch. Good scenario, we can let the main thread of the application continue execution after other threads are ready.

To give an example in life, it is the scene of athletes running. For example, when there are 5 athletes participating in a race and there is a referee at the finish line, when does the race end? That is, when everyone has reached the finish line, this is equivalent to waiting for the referee to wait for all five athletes to reach the finish line and announce the end of the game. We use the form of code to write the athlete running scene, the code is as follows:

public class RunDemo1 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch latch = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
    
    
            final int no = i + 1;
            Runnable runnable = new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(no + "号运动员完成了比赛。");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        latch.countDown();
                    }
                }
            };
            service.submit(runnable);
        }
        System.out.println("等待5个运动员都跑完.....");
        latch.await();
        System.out.println("所有人都跑完了,比赛结束。");
    }
}

In this code, we create a new CountDownLatch with an initial value of 5, and then create a thread pool with fixed 5 threads, and use a for loop to submit 5 tasks to this thread pool. Each task represents an athlete. This The athlete will first randomly wait for a period of time to represent that he is running, and then print out that he has completed the race. After running, it will also call the countDown method to decrement the count by 1.

After that, we return to the main thread. After the main thread prints the sentence "Waiting for 5 athletes to finish running", it will call the await() method, which means that the main thread will start to wait, and the sub-threads before the wait will be executed. After it is over, it will think that everyone has finished the race. The running result of this program is as follows:

等待5个运动员都跑完.....
4号运动员完成了比赛。
3号运动员完成了比赛。
1号运动员完成了比赛。
5号运动员完成了比赛。
2号运动员完成了比赛。
所有人都跑完了,比赛结束。

It can be seen that the main thread will not continue until the five athletes have completed the game, and since the waiting time of the child threads is random, the order in which each athlete completes the game is also random.

Usage 2: Multiple threads wait for the signal of a certain thread and start execution at the same time

This is a bit opposite to the first usage. Let's take another actual scenario. For example, in a sports meeting, we just said that referees waited for athletes, but now they are athletes waited for referees. Before the athletes start to run, they will wait for the referee to give orders and give the athletes a unified start. We use the code to describe this matter, as shown below:

public class RunDemo2 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        System.out.println("运动员有5秒的准备时间");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ExecutorService service = 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 + "号运动员准备完毕,等待裁判员的发令枪");
                    try {
    
    
                        countDownLatch.await();
                        System.out.println(no + "号运动员开始跑步了");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            };
            service.submit(runnable);
        }
        Thread.sleep(5000);
        System.out.println("5秒准备时间已过,发令枪响,比赛开始!");
        countDownLatch.countDown();
    }
}

In this code, first print out that the athlete has 5 seconds of preparation time, and then create a new CountDownLatch, whose reciprocal value is only 1. Then, it is also a 5-thread thread pool, and submit it in a for loop. 5 tasks, and these 5 tasks let it call the await() method to start waiting at the beginning.

Next we return to the main thread. The main thread will first wait for 5 seconds, which means that the referee is making preparations, for example, he will shout "all in place, ready"; then 5 seconds later, the main thread will print out "5 seconds of preparation time has passed , The starting gun sounds, the game starts" signal, and then the countDown method will be called. Once the main thread calls this method, the 5 threads that have called the await() method will all be awakened, so the running result of this program as follows:

运动员有5秒的准备时间
2号运动员准备完毕,等待裁判员的发令枪
1号运动员准备完毕,等待裁判员的发令枪
3号运动员准备完毕,等待裁判员的发令枪
4号运动员准备完毕,等待裁判员的发令枪
5号运动员准备完毕,等待裁判员的发令枪
5秒准备时间已过,发令枪响,比赛开始!
2号运动员开始跑步了
1号运动员开始跑步了
5号运动员开始跑步了
4号运动员开始跑步了
3号运动员开始跑步了

It can be seen that the athletes will first have 5 seconds of preparation time, and then the 5 athletes are ready to wait for the starting gun to fire, and then 5 seconds later, the starting gun is fired and the game starts, so the 5 sub-threads start running almost at the same time .

important point

Let's talk about the points of attention of CountDownLatch:

  • I just talked about two usages. In fact, these two usages are not isolated. You can even combine the two usages. For example, you can use two CountDownLatch, the first initial value is multiple, and the second initial value is 1. In this way, more complex business scenarios can be dealt with;
  • CountDownLatch cannot be reused. For example, if the countdown has been completed, can we continue to countdown again next time? This is impossible. If you have this requirement, you can consider using CyclicBarrier or create a new CountDownLatch instance.
to sum up

When creating an instance of the CountDownLatch class, you need to pass in the number of countdowns in the constructor, and then the thread that needs to wait calls the await method to start waiting, and every time other threads call the countDown method, the count will be decremented by 1 until it is decremented When it is 0, the thread that was waiting will continue to run

Guess you like

Origin blog.csdn.net/Rinvay_Cui/article/details/111056673