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激活主线程。然后完成后边的计算。否则主线程将一直处于等待状态。
- 49. 访问PostgreSQL数据库增删改查 | 厚土Go学习笔记
- Golang中Interface类型详解
- Go语言的网络编程简介
- 一条关于swap争用的报警邮件分析(二)(r8笔记第4天)
- Golang泛型编程初体验
- 厚土Go学习笔记 | 14. switch 的条件写的有点灵活,不过风格还是go的一贯风格
- Go语言·我的性能我做主
- 47. 访问MySql数据库实现增删改查 | 厚土Go学习笔记
- system表空间不足的问题分析(二) (r8笔记第5天)
- golang基于redis lua封装的优先级去重队列
- python基础知识——内置数据结构(元组)
- python基础知识——控制语句
- python基础知识——基本语法
- 11g主库归档自动删除的小问题分析 (r8笔记第1天)
- 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 数组属性和方法
- Babel配置傻傻看不懂?
- Saltstack_使用指南10_配置管理-状态模块
- Saltstack_使用指南11_配置管理-状态之间依赖关系 4.1. 部署架构4.2. Apache和PHP的SLS【正向依赖】4.3. mariadb 的 SLS
- 前端工程化之CICD那点破事
- Saltstack_使用指南12_配置管理-jinja模板 4.1. 部署架构4.2. 本章涉及的 pillar 的信息4.3. 配置文件 httpd.conf 修改
- Saltstack_使用指南13_runner的job和manage与execution的saltutil 5.1. master执行5.2. 查看当前活动的jobs
- Saltstack_使用指南14_无master 3.1. 常规信息3.2. grains设置3.3. pillar设置
- 在CDH7.1.1中安装NiFi
- 重学前端(三)-聊聊我们的浏览器的那些事
- Saltstack_使用指南15_多master 3.1. 创建冗余的master3.2. 拷贝主master的key和配置到冗余的master3.3. 启动冗余ma
- Saltstack_使用指南16_syndic 3.1. 部署salt-master3.2. 其他配置与部署4.1. 部署salt-syndic4.2. syndic
- Centos7安装单机版MongoDB
- Saltstack_使用指南17_salt-ssh
- Saltstack_使用指南18_API
- Centos7安装单机版Redis