CountDownLatch是AQS的重要实现之一,参考AQS的使用方式与重要实现
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
CountDownLatch是通过一个计数器来实现的,计数器的初始值为初始任务的数量。每当完成了一个任务后,计数器的值就会减1(CountDownLatch.countDown()方法)。当计数器值到达0时,它表示所有的已经完成了任务,然后在闭锁上等待CountDownLatch.await()方法的线程就可以恢复执行任务。 :::success countDownLatch.countDown() 必须在finally代码块中执行。 ::: 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了,例如处理excel中多个表单。

CountDownLatch和join区别

  1. join是一定等待线程执行完成才解阻塞,CountDownLatch可以在countdown后继续执行业务代码
  2. 当线程对象Thread对象不明确的时候不能用join,即join不能配合线程池使用。

image.png

  1. public class MyTest1 {
  2. public static void main(String[] args) {
  3. CountDownLatch countDownLatch = new CountDownLatch(3);
  4. IntStream.range(0, 3).forEach(i -> new Thread(() -> {
  5. try {
  6. Thread.sleep(2000);
  7. System.out.println("hello");
  8. } catch (InterruptedException ex) {
  9. ex.printStackTrace();
  10. } finally {
  11. countDownLatch.countDown();
  12. }
  13. }).start());
  14. System.out.println("启动子线程完毕");
  15. try {
  16. countDownLatch.await();
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("主线程执行完毕");
  21. }
  22. }


public class CountDownLatchTest3 {

    public static void main(String[] args) throws InterruptedException {
        //线程池里面创建3个线程
        List<String> list = new ArrayList<>();
        list.add("Angel");
        list.add("baby");
        list.add("rose");
        list.add("joyce");

        AtomicInteger i= new AtomicInteger();
        ExecutorService executorService = Executors.newFixedThreadPool(4,(runnable)->{
            return new Thread(runnable,list.get(i.getAndIncrement()));
        });

        CountDownLatch latch = new CountDownLatch(4);
        Random random = new Random();
        for (int j = 0; j <4 ; j++) {//new 4个线程  并发执行
            int temp =j;
            executorService.submit(()->{
                //k标识的是准备进度 直到准备到100% 
                for (int k = 0; k <100 ; k++) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(random.nextInt(200));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    String name = Thread.currentThread().getName();
                    name=name+"("+k+"%)";//angel(3%) baby(10%) ...
                    list.set(temp,name);
                    System.out.print("\r"+ Arrays.toString(list.toArray()));
                }
                latch.countDown();
            });
        }
        latch.await();
        System.out.println("\n 结束...");
        executorService.shutdown();
    }
}