java:用CountDownLatch.await替代Object.wait实现线程阻塞/唤醒
版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/53700088
线程之间经常需要一定的同步,比如线程A需要线程B的部分运算结果,但又不必等到线程B把所有的结果都算出来,否则A就要待太长时间。 下面这个例子就是这个应用场景,主线程需要等待子线程从数据库中加载记录,但是子线程把所有的记录都加载完要花挺长时间。 而实际上,主线程最开始只需要一条记录就可以继续自己的后续动作了。怎么办呢?下面的代码利用传统的Object.wait()/nofity()方法来实现:
public void openSource() {
// 创建一个初值为1的倒数计数器对象作为通知对象
Object notifier=new Object();
// 启动新线程用于长时间的加载任务
new Thread(new Runnable() {
@Override
public void run() {
boolean isNotified=false;
try {
int index = 0;
while (true) {
{
// 加载数据。。。。
}
// 加载第一条记录后,唤醒等待线程
if (1 == ++index) {
synchronized (notifier){
notifier.notify();
isNotified=true;
}
}
}
} finally {
//循环结束,不论有没有加载到数据记录,都执行唤醒,
//以确保notify无论如何都会被执行一次,否则等待线程会一直阻塞
synchronized (notifier){
if(!isNotified)
notifier.notify();
}
}
}
}).start();
synchronized (notifier){
try {
// 启动子线程后立即阻塞, 等待被子线程唤醒
notifier.wait();
} catch (InterruptedException e) {}
}
// 确保子线程加载了第一记录后,主线程继续自己的工作。。。。
}
话说,用Object.wait()/notify()倒是挺直观,但是,这synchronized同步块代码写起来实在有点啰嗦,我是个一行代码都不想多写的懒人。 就在想这代码能不能 看着更简单点? 于是想到了java.util.concurrent包下的CountDownLatch, 这是个好东西,顾名思义,它实现了一个多线程环境下倒数计数器锁,当计数器倒数到0时,唤醒阻塞的线程,允许多个线程同时对计数器减1,用在这里实在大材小用了。不过管它呢,方便就成啊。 于是前面的代码就被我用CountDownLatch改造成下面这样:
public void openSource() {
// 创建一个初值为1的倒数计数器锁
CountDownLatch notifier=new CountDownLatch(1);
new Thread(new Runnable() {
@Override
public void run() {
try {
int index = 0;
while (true) {
{
// 加载数据。。。。
}
if (1 == ++index) {
// 计数减1,唤醒等待线程
notifier.countDown();
}
}
} finally {
// 线程结束之前再执行一次唤醒动作
notifier.countDown();
}
}
}).start();
try {
// 启动子线程后立即阻塞, 等待被倒数计数器锁唤醒
notifier.await();
} catch (InterruptedException e) {}
}
相比前面的代码,finally{}代码块中没有再判断是否已经执行过唤醒动作,为什么呢,因为countDown当计数已经归0的时候什么也不做,所以就算多执行一次countDown也完全不影响程序的逻辑。 没有烦人的synchronized同步块代码块后,代码是不是看起来简洁一点点呢? 当然相比原始的wait/nofity,CountDownLatch的实现是经过高度封装的代码,最终它也是用wait/nofity来实现的,但逻辑更复杂,所以性能上有多少影响我并不清楚,因为我的应用环境是在程序初始化的时候,并不是高频调用,所以我并不太关注这个。
- 数据可视化-EChart2.0使用总结2
- rpc框架之 thrift 学习 2 - 基本概念
- rpc框架之avro 学习 1 - hello world
- 探讨Android中的内置浏览器和Chrome
- java并发编程学习: 阻塞队列 使用 及 实现原理
- CSS魔法堂:说说Float那个被埋没的志向
- Netbeans配置Xdebug
- rpc框架: thrift/avro/protobuf 之maven插件生成java类
- WebComponent魔法堂:深究Custom Element 之 从过去看现在
- 数据可视化-EChart2.0使用总结1
- JavaScript事件概览
- gradle项目与maven项目相互转化
- rpc框架之gRPC 学习 - hello world
- Angular Service入门
- 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 文档注释
- Nginx系列:安全下载模块
- 5分钟入门GANS:原理解释和keras代码实现
- 使用ML 和 DNN 建模的技巧总结
- 医学图像分割模型U-Net介绍和Kaggle的Top1解决方案源码解析
- 机器学习中的音频特征:理解Mel频谱图
- 兄弟,如何淡定地渡过七夕?
- Spring 源码第 9 篇,深入分析 FactoryBean
- PowerBI 动态数据格式 高级版 以及重要通知
- 气哭老板的顶级密钥存放方案,又做了一件蠢事
- 构建没有数据集的辣辣椒分类器,准确性达到96%
- 由 Redis 分布式锁造成的重大事故
- 10分钟搞定 Java 并发队列好吗?好的
- MySQL 案例:关于程序端的连接池与数据库的连接数
- spark和kafka jar包冲突NoSuchMethodError: net.jpountz.lz4.LZ4BlockInputStream
- 聊聊claudb的scripting command