CountDownLatch is a useful utility class that allows you to hold one or more threads until they are ready to execute.

Speaking of which, let me give you a typical example: suppose an assembly line has three workers: Worker0, worker1, and worker2. There is a task that requires all three of them to work together to complete. Worker2 can start this task if worker0 and Worker1 have completed their work, and worker0 and Worker1 can work in parallel.

If you use normal thread blocking, I think you can easily think of using join to do this. When the join() method of a thread is called in the current thread, the current thread blocks until the thread completes.

If implemented this way, the code would look like this:

public class Worker extends Thread { private String name; private long time; public Worker(String name, long time) { this.name = name; this.time = time; } @override public void run() {try {system.out.println (name+" start work "); Thread.sleep(time); System.out.println(name+" work done, elapsed time ="+time); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Then we add a test method:

Public class Test {public static void main(String[] args) throws InterruptedException {// TODO automatically generates the method stub Worker worker0 = new Worker(" worker0 ", (long) (Math.random()*2000+3000)); Worker worker1 = new Worker("worker1 ", (long) (Math.random()*2000+3000)); Worker worker2 = new Worker("worker2 ", (long) (Math.random()*2000+3000)); worker0.start(); worker1.start(); worker0.join(); // block worker0 worker1.join(); // Call join block worker1 system.out.println (" all ready "); worker2.start(); }}Copy the code

Then run the above code and we can see that the above results are satisfied.

In addition, we can also use CountDownLatch to achieve the above effects, which brings us to one of the implementation principles of CountDownLatch.

CountDownLatch

The CountDownLatch class is a concurrency utility class located under the java.util.concurrent package, implemented with a counter whose initial value is the number of threads.

Each time a thread completes its task, the counter is reduced by one. When the counter value reaches 0, it indicates that all threads have completed the task, and the threads waiting on the lock can resume the task. That is, the count in the constructor is actually the number of threads to wait for the latch. This value can only be set once, and CountDownLatch does not provide any mechanism to reset the count. When the number of countdownlatches reaches zero, other threads take advantage of the opportunity to execute.

The first interaction with CountDownLatch is for the main thread to wait for another thread, which must call the countdownlatch.await () method immediately after starting the other thread. Operations on the main thread will block on this method until the other threads finish their work.

For example, for the example at the beginning of the article, to achieve the same effect, we need to make the following changes.

public class Worker extends Thread { private String name; private long time; private CountDownLatch countDownLatch; public Worker(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } @override public void run() {try {system.out.println (name+" start work "); Thread.sleep(time); System.out.println(name+" work done, elapsed time ="+time); countDownLatch.countDown(); System.out.println(" countDownLatch.getCount()="+countDownLatch.getCount()); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Then, we write a test case:

public class Test { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch =  new CountDownLatch(2); Worker worker0 = new Worker(" worker0 ", (long) (Math.random()*2000+3000), countDownLatch); Worker worker1 = new Worker("worker1 ", (long) (Math.random()*2000+3000), countDownLatch); Worker worker2 = new Worker("worker2 ", (long) (Math.random()*2000+3000), countDownLatch); worker0.start(); worker1.start(); Countdownlatch.await () countdownlatch.await (); System.out.println(" ready "); worker2.start(); }}Copy the code

Consider the following application scenario: Assuming that worker’s work can be divided into two stages, work2 only needs to wait for Work0 and Work1 to complete the first stage of their respective work before starting their own work, instead of waiting for Work0 and Work1 to complete their work in scenario 1.

In this case, a JOIN cannot implement this scenario, whereas CountDownLatch does, because it holds a counter, and as long as the counter is zero, the main thread can terminate the block and proceed. The relevant codes are as follows:

public class Worker extends Thread { private String name; private long time; private CountDownLatch countDownLatch; public Worker(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } @override public void run() {try {system.out.println (name+" start work "); Thread.sleep(time); System.out.println(name+" phase 1 work done "); countDownLatch.countDown(); Thread.sleep(2000); System.out.println(name+" phase 2 work done "); System.out.println(name+" work done, elapsed time ="+(time+2000)); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Test method:

public class Test {  
  
    public static void main(String[] args) throws InterruptedException {  
  
        CountDownLatch countDownLatch = new CountDownLatch(2);  
        Worker worker0 = new Worker(" worker0 ", (long) (Math.random()*2000+3000), countDownLatch);  
        Worker worker1 = new Worker("worker1 ", (long) (Math.random()*2000+3000), countDownLatch);  
        Worker worker2 = new Worker("worker2 ", (long) (Math.random()*2000+3000), countDownLatch);  
          
        worker0.start();  
        worker1.start();      
        countDownLatch.await();  
          
        System.out.println("准备工作就绪 ");  
        worker2.start();  
          
    }  
}  Copy the code

Run the above test case and see the output that meets our criteria:

Worker0 Start Worker1 Start Worker1 Phase 1 Work completed Worker0 Phase 1 work completed Preparations Are ready Worker2 Start Worker1 Phase 2 work completed Worker1 Work completed, Worker0 Completed, Consumed Time =6147 Worker2 Completed in the first stage, Completed in the second stage, Consumed Time =5384Copy the code