理解Java并发工具类CountDownLatch
CountDownLatch相信大家并不陌生,我们在上篇文章中已经分析其实现,这里在简单回顾一下CountDownLatch是基于AQS共享锁构建的一种同步器,它的主要应用场景有两种:
(1)一个线程等待所有的其他线程执行完任务之后自己再执行自己的任务。
(2)多个线程等待一个线程执行完任务之后,然后多个线程同时开始执行自己的任务。
在实际开发中,可能大家仅仅对第一种场景比较熟悉,而完全忽视了第二种场景,实际上第二种场景才是CountDownLatch发挥共享锁的真正案例。
CountDownLatch的方法主要是:
(1)构造方法:
CountDownLatch(int count)
参数count控制线程的数量
(2)await()
阻塞当前调用的线程,直到count的值等于0才唤醒,除非执行了线程中断,否则
在没到达0之前,一直处于waiting状态
(3)await(long timeout, TimeUnit unit)
阻塞当前调用的线程,直到count的值等于0才唤醒,除非执行了线程中断或者指定的时间周期过期,否则在没到达0之前,一直处于waiting状态
(4)countDown()
每次调用对count的值减1,当这个值到达0的时候,会释放所有等待的线程。
(5)getCount()
返回当前count的数量
下面我们看一个比较典型的一个例子:
package concurrent.tools;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
/**
* Created by Administrator on 2018/8/20.
*/
public class CountDownDemo2 {
static class Worker implements Runnable{
private final CountDownLatch startSignal;
private final CountDownLatch dongSignal;
Worker(CountDownLatch startSignal,CountDownLatch dongSignal){
this.startSignal=startSignal;
this.dongSignal=dongSignal;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" 启动了,等待main线程调度.......");
startSignal.await();
doWork();
System.out.println(Thread.currentThread().getName()+" 完活 ..... ");
dongSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void doWork() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+" 开始工作 ..... ");
Thread.sleep(5000);
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal=new CountDownLatch(1);//
CountDownLatch doneSignal=new CountDownLatch(5);
for(int i=0;i<5;i++){
new Thread(new Worker(startSignal,doneSignal)).start();
}
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName()+"线程准备就绪,所有线程可以开始工作了..... ");
startSignal.countDown();
doneSignal.await();
System.out.println(Thread.currentThread().getName()+"线程监控任务结束 ");
}
}
执行完成之后,输出结果如下:
Thread-0 启动了,等待main线程调度.......
Thread-2 启动了,等待main线程调度.......
Thread-1 启动了,等待main线程调度.......
Thread-3 启动了,等待main线程调度.......
Thread-4 启动了,等待main线程调度.......
main线程准备就绪,所有线程可以开始工作了.....
Thread-0 开始工作 .....
Thread-2 开始工作 .....
Thread-4 开始工作 .....
Thread-3 开始工作 .....
Thread-1 开始工作 .....
Thread-2 完活 .....
Thread-3 完活 .....
Thread-4 完活 .....
Thread-1 完活 .....
Thread-0 完活 .....
main线程监控任务结束
上面的例子就是一个非常典型的例子,反应到实际生活的场景比如,张三生日party,准备在一个大酒店进行,客房里面有4个服务员等待服务上菜,前提是张三所有的朋友必须到齐才能开始宴会,然后张三就是协调者,在所有朋友到齐之后,张三发话开始上菜,这时候4个服务员就可以去同时进行上菜。这个例子里面就和上面我们代码执行的例子非常类似。此外还有在web服务器中,必须等缓存初始化之后,我们的程序才对外提供服务,那么这个场景也可以使用CountDownLatch来完成。
这里大家需要避免一个误区,大多数时候我们都是多个线程调用 countDown,只有一个线程调用await, 但实际情况是await方法也是可以有多个线程调用的,而这正是共享锁的体现。
关于CountDownLatch使用的几个步骤:
(1)构造函数指定需要等待的线程数量
(2)对于执行countDown方法的线程为了安全起见这个调用必须写在finally块里面,防止线程发生异常退出,导致程序永远不会终止。
(3)对于异常终止判断,我们可以通过一个布尔变量或者CountDownLatch的getCount方法来判断是不是有的任务异常退出,从而决定需要做什么
@Override protected void onKernalStart0()
throws IgniteCheckedException {
try {
queueHdrView = cctx.cache();
initFlag = true;
}
finally {
initLatch.countDown();
}
}
(4)对于执行await方法的线程,我们需要判断是否有效,如果无效则要抛出终端异常。
public static void await(CountDownLatch latch)
throws IgniteInterruptedCheckedException {
try {
if (latch.getCount() > 0)
latch.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IgniteInterruptedCheckedException(e);
}
}
最后需要注意的是CountDownLatch仅仅只能被用一次,不能被重置,如果需要循环重置则需要使用Java并发工具包的另外一个类CyclicBarrier。这个会在下一篇文章中介绍。
- 从MapX到MapXtreme2004[7]-对Table、Feature等的理解
- 互联网赋能传统装企 “科技撬动力巨大”
- Python接口自动化-8-测试报告
- http应用优化和加速说明-负载均衡
- linux负载均衡总结性说明(四层负载/七层负载)
- 从MapX到MapXtreme2004[6]-标点心得
- silverlight3中的"伪"3D
- 暴利驱动的疯狂游戏“外挂”:非法获利可达数百万;X-Agent 后门大升级,俄罗斯 APT28 间谍活动更为隐蔽
- Nginx+keepalived双机热备(主从模式)
- 即使不做程序员,也要学会像程序员一样去思考
- 这5项高科技 正在颠覆未来医疗
- xml的解析
- “微信身份证”来了!下月起全国推广!
- silverlight3的"伪"3D续--图片横向轮换
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释