CountDownLatch源码解析
CountDownLatch看着字面意思根本不知道这个类的大概作用是什么。。通过查阅相关资料,得出的结论是这个类能够让当前线程在其他线程都执行完毕之后再执行。那么它是怎么做到这点的呐?其实按照之前的AQS,通过判断队列中的节点个数是否为0也可以做哦!那么他是否采用这种方式?
看一下CountDownLatch的方法,发现这块代码也是很少。而切中就是一个内部类Sync,而Sync就是继承了大名鼎鼎的AQS,看来确实跟AQS有着千丝万缕的关系。那么毫无疑问,这些方法都是最终都调用的Sync继而调用AQS。
这里用一下网上流传的例子:
public class MultiThread {
private static ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(10);
private static CountDownLatch latch;
private static final int ThreadNum = 10;
static {
for (int i = 0; i < 10; i++) {
blockingQueue.add("test" + i);
}
latch = new CountDownLatch(10);
}
/**
* 用blockQueue中的元素模拟文件分片
* @return
*/
public static String getFileSplit() {
return blockingQueue.poll();
}
static class myThread implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName() + "begin running...");
String m = getFileSplit();
System.out.println(m);
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println(Thread.currentThread().getName() + "ended");
}
}
public static void main(String args[]){
System.out.println("主线程开始运行");
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i=0;i<ThreadNum;i++){
service.execute(new Thread(new myThread()));
}
service.shutdown();
System.out.println("线程已经全部运行");
System.out.println("等待所有线程运行结束");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程退出");
}
}
确实实现了效果,但是每次进行一个latch.countDown();如果不进行这个操作,那么程序就会陷入等待。那么我们看看这块是怎么实现的。
在初始化函数中,我们看到传入了线程的数量,也就是Sync持有的锁的数量。这和之前分析的各种重入锁是一样的。只是现在一次性全赋值了。
在countDown方法中,我们看到还是调用的AQS的释放锁的函数。
getCount方法也是调用的AQS的方法unparkSuccessor,而unparkSuccessor的主要作用就是唤醒这里的主线程(这里逻辑比较复杂,之前已经分享过了吧,大概就是任务队列向头节点轮询,该跳过的跳过,该抛弃的抛弃,然后取出第一个进行激活,让其运行)。感觉本例中要明确的写死state的方式挺难的,因为现实中很多都是动态的。难道在现实问题中,就只能坐以待毙?还是countDownLatch只能解决局部可解的有限问题?我感觉是这么回事
总结:
CountDwonLatch都是调用的AQS的方法,类的本体方法很少。通过初始化的时候确定执行的线程任务来跑。能够在小范围的计算数据,比如对数据进行批量进行数据处理,然后再进行数据的结果的合并等。
CountDownLatch的使用一般为首先初始化AQS的锁数量(state),然后当一个线程完成任务的时候,都进行一次countDown对state进行减一操作。在最后所有线程做完之后,需要主线程进行运行时,需要进行await操作,在await方法中使用AQS的unparkSuccessor激活主线程。然后完成后边的计算。否则主线程将一直处于等待状态。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Hadoop【单机安装-测试程序WordCount】
- MySQL Shell转储和加载第4部分:转储实例和模式
- 【创建模式】--工厂模式
- 【创建模式】--Singleton
- Mapreduce 历史服务 配置启动查看
- Yarn 的日志聚集功能配置使用
- HDFS 的垃圾回收配置
- 配置SSH无密码登录
- OpenCV学习一《Linux下安装OpenCV》
- InnoDB的数据锁–第1部分“简介”
- Kafka中的再均衡
- 基础教程 | Tomcat 中使用 Jenkins
- GSoC: GitHub Checks API 项目第三阶段总结
- Sql注入基础_mysql注入
- WEB 安全学习 一、mysql 注入漏洞