CountDownLatch两个典型用法

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

CountDownLatch直译过来是倒计时门闩,其两个典型用法要么为一等多,要么为多等一,这里一一演示这两种用法,需要注意 CountDownLatch不能重用,如果需要重用可以考虑使用CyclicBarrier

用法 1(一等多)

一个线程等待多个线程都执行完毕,再继续执行自己的工作

 package org.example.concurrent;
 ​
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 ​
 /**
  * 本类用于演示 CountDownLatch 的一个用法
  * 一个线程等待其他线程都完成后继续进行后续的工作
  * 这里假设主线程需要等待 5 个子线程都完成检查后再进行到下一个阶段
  *
  * @author Catch
  */
 public class CountDownLatchUsage1 {
 ​
     public static void main(String[] args) throws InterruptedException {
         CountDownLatch latch=new CountDownLatch(5);
         ExecutorService executorService = Executors.newFixedThreadPool(5);
         for (int i = 0; i < 5; i++) {
             int no=i+1;
             Runnable r=()->{
                 try {
                     Thread.sleep((long) (Math.random()*10000));
                     System.out.println("No."+no+"完成了检查工作");
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }finally{
                     latch.countDown();
                 }
             };
             executorService.submit(r);
         }
         System.out.println("等待 5 个子线程完成检查工作...");
         latch.await();
         System.out.println("所有子线程都完成了检查工作, 开始进入下一个阶段.");
     }
 }
复制代码

用法 2(多等一)

下面是多个线程等待一个线程完成工作,然后多个线程同时开始工作

 package org.example.concurrent;
 ​
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 ​
 /**
  * 本类用于演示 CountDownLatch 的一个用法
  * 多个线程等待一个线程完成工作,然后多个线程同时开始工作
  * 这里假设我们要进行一次压测, 首先准备 1000 个线程,
  * 当 1000 个线程都准备完毕后同时发起请求,来达到模拟高峰期用户访问的效果
  *
  * @author Catch
  */
 public class CountDownLatchUsage2 {
 ​
     public static void main(String[] args) throws InterruptedException {
         CountDownLatch begin=new CountDownLatch(1);
         CountDownLatch end = new CountDownLatch(1000);
 ​
         ExecutorService executorService = Executors.newFixedThreadPool(1000);
         for (int i = 0; i < 1000; i++) {
             Runnable r=()->{
                 try {
                     // 可以在 begin.await() 前进行一些准备或初始化工作
                     begin.await();
                     // 模拟压测耗时, 有的快有的慢
                     Thread.sleep((long) (Math.random() * 5000));
                     System.out.println(Thread.currentThread().getName()+"发起请求...");
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }finally{
                     end.countDown();
                 }
             };
             executorService.submit(r);
         }
         Thread.sleep(3000);
         System.out.println("所有线程已准备完毕, 压测开始!");
         begin.countDown();
 ​
         end.await();
         System.out.println("所有线程都请求完毕, 压测结束!");
     }
 }
复制代码