- 1、CountDownLatch的作用
- 2、CountDownLatch使用规则
- 3、CountDownLatch的Demo
- 4、CountDownLatch原理 (待补充)
- 5、CountDownLatch的实现 (待补充)
1、CountDownLatch的作用
在JDK 5 以后sun公司引入了大量设计来解决并发的问题,学习使用这些类,我们可以更加自如的创建简单而健壮的并发程序,CountDownLatch类是用来同步一个或者多个任务的工具类,强制它们等待由其他任务执行的一组操作。
例如,现在有这样的一个需求,为了程序的快速运行,我们把一个程序分为三部分,分别是初始化、数据拉取、显示数据。初始化操作和数据拉取操作不会冲突,可以说这两个操作完全不相关,但是显示数据必须要在上面两个任务完成以后才可以继续。如果把这三个操作至于不同的线程,那么如果不对线程的同步加以控制的话,必将大乱,这个时候我们就可以先对CountDownLatct初始计数器为2,然后让初始化操作和数据拉取操作开始运行,然后在结束的时候调用countDown方法,然后显示操作在run开始的时候使用awit进行等待,直到计数器归0,显示操作开始执行,而这个时候上述两个操作都已经执行完毕。
2、CountDownLatch的使用规则
我们可以向CountDownLatch对象设置一个初始计数值,任何在CountDownLatch对象上调用wait的方法都将被阻塞,直到这个计数器达到0。其他任务在结束其工作时,可以在该对象上调用countDown()来减少这个计数器。CountDownLatch被设计为只触发一次,计数器不能被重置。
调用countDown的任务在产生这个调用时并没有被阻塞,只有对await的调用会产生阻塞,直到计数器达到0为止。
CountDownLatch的典型用法是将一个程序分为n个互相独立的可以解决的任务,并创建值为0的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown。等待问题被解决的任务在这个锁存器上面调用await,将它们自己揽住,直至锁存器计数结束,下面通过一个例子来演示该框架应该如何应用。
个人理解:CountDownLatch:我把他理解成倒计时锁
场景还原:一年级期末考试要开始了,监考老师发下去试卷,然后坐在讲台旁边玩着手机等待着学生答题,有的学生提前交了试卷,并约起打球了,等到最后一个学生交卷了,老师开始整理试卷,贴封条,下班,陪老婆孩子去了。
补充场景:我们在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面。
3、CountDownLatch的Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
public class TestCountDownLatch { static final int SIZE = 10 ; public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); CountDownLatch countDownLatch = new CountDownLatch(SIZE); for (int i = 0; i < 10; i++) { exec.execute(new WaitingTask(countDownLatch)); } for (int i = 0; i < SIZE; i++) { exec.execute(new TaskProtion(countDownLatch)); } System.out.println("Launch all task"); exec.shutdown(); } } class TaskProtion implements Runnable { private static int sCounter = 0 ; private final int id = sCounter ++ ; private static Random sRandom = new Random(47) ; private final CountDownLatch mLatch ; public TaskProtion(CountDownLatch latch) { this.mLatch = latch ; } @Override public void run() { try { doWork(); mLatch.countDown(); } catch (InterruptedException e) { System.out.println("TaskProtion is exiting form intrrupt"); } } public void doWork() throws InterruptedException { TimeUnit.MICROSECONDS.sleep(sRandom.nextInt(2000)); System.out.println(this + " completed"); } @Override public String toString() { return String.format("%1$-3d", id); } } class WaitingTask implements Runnable { private static int sCounter = 0 ; private final int id = sCounter ++ ; private final CountDownLatch mLatch ; public WaitingTask (CountDownLatch latch) { this.mLatch = latch ; } @Override public void run() { try { this.mLatch.await(); System.out.println("Latch barrier passed for " + this); } catch (InterruptedException e) { System.out.println(this + " interrupted"); } } @Override public String toString() { return String.format("WaitingTask %1$-3d", id); } } Launch all task 2 completed 8 completed 6 completed 5 completed 3 completed 9 completed 4 completed 0 completed 7 completed 1 completed Latch barrier passed for WaitingTask 3 Latch barrier passed for WaitingTask 6 Latch barrier passed for WaitingTask 0 Latch barrier passed for WaitingTask 1 Latch barrier passed for WaitingTask 8 Latch barrier passed for WaitingTask 9 Latch barrier passed for WaitingTask 7 Latch barrier passed for WaitingTask 2 Latch barrier passed for WaitingTask 4 Latch barrier passed for WaitingTask 5
|
通过上述的任务输出,我们不难看出,我们将我们的任务很整齐的进行了先后顺序的控制,让我们程序当中有预支条件的任务可以在预支条件之后进行运行。这里就是通过CountDownLatch的锁存器当中的计数器来控制的。这里需要注意的两点就是,
- CountDownLatch这个类当中的计数器的初始值,只能被初始化一次,不能初始化第二次,
- 在使用的时候,必须多个对象同步同一个CountDownLatch这个类。
我们可以尝试将上述代码当中的CountDownLatch相关操作去除,我们再来看一下运行结果,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Launch all task Latch barrier passed for WaitingTask 9 Latch barrier passed for WaitingTask 1 Latch barrier passed for WaitingTask 2 Latch barrier passed for WaitingTask 8 Latch barrier passed for WaitingTask 5 9 completed 2 completed Latch barrier passed for WaitingTask 6 5 completed 8 completed 3 completed Latch barrier passed for WaitingTask 3 1 completed 6 completed Latch barrier passed for WaitingTask 4 7 completed Latch barrier passed for WaitingTask 7 Latch barrier passed for WaitingTask 0 0 completed 4 completed
|
很明显结果杂乱无章,没有任何规律可言,到这里大家应该明白了CountDownLatch类的使用了吧。