并发编程学习笔记(十九、CountDownLatch源码分析)

目录:

  • CountDownLatch是什么
  • 为什么要有CountDownLatch
  • CountDownLatch源码分析

CountDownLatch是什么

CountDownLatch是一种闭锁,也叫倒数计数器,它可以等待多个线程执行完毕后再执行某一件事情。

比如你app的首页要加载很多个模块,而这些模块又处于不同服务,这时候你就可以开启多个线程去分别调用这些模块;然后你后续的一个操作需要用到刚刚调用的那些服务返回的数据,这时候你就可以用CountDownLatch了。

为什么要有CountDownLatch

针对上面那个问题你可能会说,那直接用Thread.join()不就可以了嘛。当然就简单的场景来说,join()的确就够用了,而CountDownLatch其实也就是对join()的一种扩展,它可以适用于更复杂的场景。

我们通过一段代码来看看为何CountDownLatch的控制粒度更细(为了方便我把两个demo的代码写一块了)。

 1 public class ThreadA extends Thread {
 2 
 3     private CountDownLatch countDownLatch;
 4 
 5     public ThreadA() {
 6     }
 7 
 8     public ThreadA(CountDownLatch countDownLatch) {
 9         this.countDownLatch = countDownLatch;
10     }
11 
12     @Override
13     public void run() {
14         System.out.println("threadA start...");
15 
16         try {
17             System.out.println("threadA 开始执行第一个任务...");
18             TimeUnit.SECONDS.sleep(1);
19             System.out.println("threadA 第一个任务执行完毕...");
20         }
21         catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24         finally {
25             if (countDownLatch != null) {
26                 countDownLatch.countDown();
27             }
28         }
29 
30         try {
31             System.out.println("threadA 开始执行第二个任务...");
32             TimeUnit.SECONDS.sleep(3);
33             System.out.println("threadA 第二个任务执行完毕...");
34         }
35         catch (InterruptedException e) {
36             e.printStackTrace();
37         }
38         finally {
39             // do something...
40         }
41 
42         System.out.println("threadA end...");
43     }
44 
45 }
 1 public class ThreadB extends Thread {
 2 
 3     private CountDownLatch countDownLatch;
 4 
 5     public ThreadB() {
 6     }
 7 
 8     public ThreadB(CountDownLatch countDownLatch) {
 9         this.countDownLatch = countDownLatch;
10     }
11 
12     @Override
13     public void run() {
14         System.out.println("threadB start...");
15 
16         try {
17             System.out.println("threadB 开始执行第一个任务...");
18             TimeUnit.SECONDS.sleep(2);
19             System.out.println("threadB 第一个任务执行完毕...");
20         }
21         catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24         finally {
25             if (countDownLatch != null) {
26                 countDownLatch.countDown();
27             }
28         }
29 
30         try {
31             System.out.println("threadB 开始执行第二个任务...");
32             TimeUnit.SECONDS.sleep(5);
33             System.out.println("threadB 第二个任务执行完毕...");
34         }
35         catch (InterruptedException e) {
36             e.printStackTrace();
37         }
38         finally {
39             // do something...
40         }
41 
42         System.out.println("threadB end...");
43     }
44 
45 }
 1 public class JoinTest {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         ThreadA threada = new ThreadA();
 5         ThreadB threadb = new ThreadB();
 6         threada.start();
 7         threadb.start();
 8         threada.join();
 9         threadb.join();
10         System.out.println("joinTest end...");
11     }
12 
13 }
 1 public class CountDownLatchTest {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         CountDownLatch countDownLatch = new CountDownLatch(2);
 5         ExecutorService executorService = Executors.newCachedThreadPool();
 6         executorService.submit(new ThreadA(countDownLatch));
 7         executorService.submit(new ThreadB(countDownLatch));
 8         countDownLatch.await();
 9         System.out.println("countDownLatchTest end...");
10         executorService.shutdown();
11     }
12 
13 }

为了方便我直接把countDownLatch.countDown()写到一个thread里面了,哈哈。

首先我们观察ThreadA与ThreadB,可以发现他们分别有两个任务需要完成;如果你的业务场景都是需要在两个线程执行完后才会处理数据,那么join和CountDownLatch都可以实现。

但如果你的场景和上述一样,只要在ThreadA、ThreadB完成第一个任务就可以处理后续逻辑的话,那么你就需要使用到CountDownLatch了;因为join没法处理那种线程只执行到某个节点就能唤醒的操作,它必须要当线程全部执行完后才能够唤醒,这就是为什么说CountDownLatch的控制粒度要比join细的原因了。

CountDownLatch源码分析

如果你把我前面所讲的AQS弄懂,那这个简直就是轻而易举,我这里就不再赘述了(偷懒了,哈哈)。

查阅源码的时候你只要记住它的实现原理就可以了,CountDownLatch可以等待多个线程执行完毕后再执行某一件事情。

posted @ 2020-06-26 22:47  被猪附身的人  阅读(168)  评论(0编辑  收藏  举报