Addition and subtraction counters of JUC Toolkit

Posted by tomd79 on Tue, 08 Feb 2022 04:13:24 +0100

1. CountDownLatch

CountDownLatch is called a subtraction counter. In practical application, according to the explanation of JDK document, there are two scenarios applicable.

Usage 1: as a starting gun, one thread holds the starting gun and orders other threads to start at the same time

Usage 2 is to observe the start or end time of all threads. For example, the bus needs to wait for all passengers to get on the bus before closing the door, or all passengers get off the bus before closing the door. That is, when all threads start or end, it starts to execute the subsequent methods of the main thread

Add: new CountDownLatch(N), coutDown(), await() must be used together. The number of times coutDown() must be executed depends on the value assigned when creating the counter object. Otherwise, the counter will not stop and other threads will not wake up. Therefore, it is necessary to ensure that the counter is cleared, Therefore, the number of calls to coutdown () must be greater than the parameter value of the constructor.

(1) Usage 1 (starting gun)

Use steps

① new CountDownLatch(1); In a thread (usually the main thread), call this method to create a subtraction counter with a value of 1

② CountDownLatch.await(); When the run method of all other threads starts, call its method to block the thread

③ CountDownLatch.countDown(); After a certain time, give an order in the main thread, call the method (subtraction counter minus one), make the counter 0, and all threads enter the execution state at the same time

Code example

public static void main(String[] args) {
        //1. Create a thread starting gun (counter with value of 1)
        CountDownLatch startSingle = new CountDownLatch(1);

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                System.out.println("I am"+Thread.currentThread().getName()+",Waiting for orders");
                try {
                    startSingle.await();    //2. Call the counter await() method to make it wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("I am"+Thread.currentThread().getName()+",Start execution");
            },"thread "+(i+1)).start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=========");
        System.out.println("I am Main Thread, shoot now");
        System.out.println("=========");
        System.out.println("boom!Run");
        startSingle.countDown(); //3. Call the countDown() method to make the counter - 1 and give the command
    }

(2) Usage 2 (record the start or end time of all threads)

Use steps

① new CountDownLatch(N); In a thread (usually the main thread), call this method to create a subtraction counter with a value of n

② countDownLatch.countDown(); In the execution method of each thread, the method is called (minus one counter minus one), so that the counter is reduced by one, recording the beginning or ending of the current thread.

③ countDownLatch.await(); Invoke the method in the main thread to block it and wait for the counter to receive a value of 0.

Code example

static void allStartOrEnd(){
        CountDownLatch allStartSingle = new CountDownLatch(5); //1. Create a subtraction counter with a value of N in the main thread
        CountDownLatch allEndSingle = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                allStartSingle.countDown(); //2. In the execution task of the thread, make the subtraction counter - 1
                System.out.println("I am"+Thread.currentThread().getName()+",Start execution");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("I am"+Thread.currentThread().getName()+",Execution completed");
                allEndSingle.countDown(); //Subtraction counter-1
            },"thread "+(i+1)).start();
        }

        try {
            allStartSingle.await(); //3. Block the main thread and leave the waiting counter empty
            try {
                Thread.sleep(50); // The main thread sleeps for a while. Avoid printing messages faster than those of threads performing tasks
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("I am the main thread. I observed that the counter value of the thread start is 0. It is known that all threads have started to execute");
            System.out.println("==============");
            allEndSingle.await();
            System.out.println("I am the main thread. I observe that the counter value of thread end is 0. It is known that all threads have ended");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

2. CyclicBarrier (addition counter)

The addition counter constructor can pass in two parameters

In general, the task to be completed at a fixed time (or fixed value) is put into the addition counter and given the value of releasing the task. When other threads are started, cyclicbarrier is called await(); Method to make the value + 1. When the value reaches the release value, the task will be executed

For example, in a car, the driver (i.e. the adder here) is required to drive only when it is full, except that the driver can seat up to 4 people. When one person gets on the car, the number of people will be + 1, and when it reaches 4, it will drive

Use steps

① new CyclicBarrier(N,new Runnable()); Create an addition counter in the main thread

② cyclicBarrier.await(); It is generally to make the counter + 1 when other threads execute

Code example

 public static void main(String[] args) {
        // 1. new CyclicBarrier(int N,new Runable());  Create an addition counter in the main thread and give it the release value and the task to be executed
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new Runnable() { //For clarity, the lamdba expression is not used
            @Override
            public void run() {
                System.out.println("==========");
                System.out.println("When the value of the addition counter reaches 5, the task is released,And the value of the addition counter is reset to 0");
            }
        });
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"In progress, the task is to set the counter value+1");
                    cyclicBarrier.await();  // 2. cyclicBarrier.await();  Make the counter + 1. The await () method here is to make the addition counter + 1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },"thread "+(i+1)).start();
        }
    }

Topics: Java Multithreading Concurrent Programming JUC