版权声明:本文是作者在学习与工作中的总结与笔记,如有内容是您的原创,请评论留下链接地址,我会在文章开头声明。 https://blog.csdn.net/usagoole/article/details/89789256
文章目录
CountDownLatch
CountDownLatch
是一个同步辅助类。jdk1.5后CountDownLatch
也可以实现join功能,并且比join得功能更多CountDownLatch
的构造函数接收一个int类型的参数作为计数器,如果想等待N个点完成就传入N。调用CountDownLatch
的countDown()
方法,N就减1,调用CountDownLatch
的await()
方法会阻塞当前线程,直到N变成0。countDown()
可以用在任何地方,所以N可以是N个线程,也可以是一个线程里的N个执行步骤。- 用在多线程时,只需要把这个
CountDownLatch
的引用传递到线程里即可 - 计数器必须大于等于0,只是等于0时候,计数器就是零,调用await方法时不会阻塞当前线程。
CountDownLatch
不可能重新初始化或者修改CountDownLatch
对象的内部计数器的值(CountDownLatch
计数无法被重置。如果需要重置计数,请考虑使用CyclicBarrier
)。一个线程调用countDown()
方法happen-before另外一个线程调用await()
方法
方法
- countDown(),当前线程调用此方法,则计数减一
- await(), 调用此方法的线程会被挂起,直到计时器的值为0才继续执行。
- await(long timeout, TimeUnit unit) 等待一定的时间后count值还没变为0的话就会继续执行
场景
- 在实时系统中的使用场景
- 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用一次countDown()方法就可以让所有的等待线程同时恢复执行。
- 开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。(一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。 只有等所有检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch)
- 死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。
案例
-
使用
CountDownLatch
实现join功能@Test public void countDownLatchJoin() { System.out.println("开始主线程" + Thread.currentThread().getName()); CountDownLatch ct = new CountDownLatch(2); new Thread(() -> { System.out.println("输入:" + 1); //调用计数器减1,countDown可以在任何地方使用 ct.countDown(); System.out.println("输入:" + 2); ct.countDown(); }).start(); try { ct.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("结束主线程" + Thread.currentThread().getName()); } 输出: 开始主线程main 输入:1 输入:2 结束主线程main
-
将准备工作分成N个部分,分给N个线程执行,每个线程执行完都调用countDown方法。当所有的子部分完成后,主线程继续执行,其实就是多个线程的join
@Test public void countDownLatchJoinMulitThread() { Driver driver = new Driver(); driver.execute(); } static class Driver { void execute() { int N = 5; CountDownLatch doneSignal = new CountDownLatch(N); final ExecutorService exec = Executors.newFixedThreadPool(N); for (int i = 0; i < N; i++) { exec.execute(new Worker(doneSignal, i)); } //等待所有线程完成处理 try { doneSignal.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } exec.shutdown(); } } static class Worker implements Runnable { private final CountDownLatch doneSignal; private final int i; public Worker(CountDownLatch doneSignal, int i) { this.doneSignal = doneSignal; this.i = i; } @Override public void run() { doWork(i); doneSignal.countDown(); // 完成准备工作,更新信号 } void doWork(int i) { System.out.println("prepare " + i + "finish"); } } 输出: prepare 0finish prepare 1finish prepare 2finish prepare 3finish prepare 4finish
-
等待所有线程执行完毕。多个
CountDownLatch
配合使用/** * 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。 */ @Test public void countDownLatchTest() { // 开始的倒数锁 final CountDownLatch begin = new CountDownLatch(1); // 结束的倒数锁 final CountDownLatch end = new CountDownLatch(10); // 十名选手 final ExecutorService exec = Executors.newFixedThreadPool(10); for (int index = 0; index < 10; index++) { final int NO = index + 1; Runnable run = new Runnable() { @Override public void run() { try { // 如果当前计数为零,则此方法立即返回。 begin.await(); Thread.sleep((long) (Math.random() * 10000)); System.out.println("线程:" + Thread.currentThread().getName() + "--No." + NO + " arrived"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 每个选手到达终点时,end就减一 end.countDown(); } } }; exec.submit(run); } System.out.println("Game Start,此时开始计数器是" + begin.getCount() + ",结束计数器是" + end.getCount()); // begin减一,开始游戏 begin.countDown(); // 等待end变为0,即所有选手到达终点 try { end.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Game Over,此时开始计数器是" + begin.getCount() + ",结束计数器是" + end.getCount()); exec.shutdown(); } 输出: Game Start,此时开始计数器是1,结束计数器是10 线程:pool-1-thread-2--No.2 arrived 线程:pool-1-thread-1--No.1 arrived 线程:pool-1-thread-6--No.6 arrived 线程:pool-1-thread-3--No.3 arrived 线程:pool-1-thread-10--No.10 arrived 线程:pool-1-thread-9--No.9 arrived 线程:pool-1-thread-8--No.8 arrived 线程:pool-1-thread-7--No.7 arrived 线程:pool-1-thread-5--No.5 arrived 线程:pool-1-thread-4--No.4 arrived Game Over,此时开始计数器是0,结束计数器是0
-
等待所有线程执行完毕。多个
CountDownLatch
配合使用@Test public void countDownLatchTest2() { Driver driver = new Driver(); driver.execute(); } static class Driver { void execute() { int N = 3; CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(N); final ExecutorService exec = Executors.newFixedThreadPool(N); for (int i = 0; i < N; i++) { exec.submit(new Worker(startSignal, doneSignal)); } //释放所有线程的等待 startSignal.countDown(); //等待所有线程完成处理 try { doneSignal.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } exec.shutdown(); } } static class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } @Override public void run() { try { startSignal.await(); // 等待开启信号 doWork(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } finally { doneSignal.countDown(); // 执行完成后更新完成信号 } } void doWork() { System.out.println("thread:" + Thread.currentThread().getName() + " work"); } }