CountDownLatch详解
功能描述
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
常见用法
多个人等一个信号后继续执行操作。例如5个运动员,等一个发令员的枪响。
一个人等多个人的信号。旅游团等所有人签到完成才开始出发。
我们最常见见到使用的地方是zk获取连接的时候
final CountDownLatch countDownLatch=new CountDownLatch(1);
ZooKeeper zooKeeper=new ZooKeeper(zk_url, time_out, new Watcher() {
public void process(WatchedEvent watchedEvent) {
Event.KeeperState state = watchedEvent.getState();
Event.EventType type = watchedEvent.getType();
if(Event.KeeperState.SyncConnected==state){
if(Event.EventType.None==type){
//调用此方法测计数减一
countDownLatch.countDown();
}
}
}
});
//阻碍当前线程进行,除非计数归零
countDownLatch.await();
这里了也可以看到很明确的使用方法,countDown直到0,await的线程才会继续执行。
原理
CountDownLatch内部使用了共享锁。如果这里还不知道共享和独占的区别,可以看前面的aqs速读。
获取锁成功的方法很简单
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
共享锁有个约定,返回有三种情况。
0为获取锁且没有其他资源
正数 获取锁并且还有其他资源
负数 获取锁资源失败
共享锁在tryAcquireShared返回大于0的值的时候,会唤醒其他停顿状态加锁线程。由于没有对state的增加操作,所以当state变成0的时候,所有尝试加锁的线程都会被唤醒。
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
释放锁的操作,就是把state的值减一,当只有state变成0的时候,才返回true,tryReleaseShared返回true的时候会触发唤醒其他加锁线程的操作。
通过上面的过程,我们可以看到CountDownLatch中的共享锁的加锁和释放锁的过程,下面看看是如何和CountDownLatch结合的。
public CountDownLatch(int count) {
if (count 0) throw new IllegalArgumentException(count 0);
this.sync = new Sync(count);
}
CountDownLatch的构造里会初始化共享锁,并且设置state的值。
public void countDown() {
sync.releaseShared(1);
}
countDown是释放锁,最终会调用到tryReleaseShared。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
await是加锁,最终会调用到 tryAcquireShared。
CountDownLatch就是一个不断释放锁的过程。
常见问题
CountDownLatch是不能够重用的
根据上面的解析,大家也发现,共享锁加锁的操作并不会增加state的值。CountDownLatch中state一旦变成0就没有提供其他方式增长回去了。
所以CountDownLatch是一次性消耗品,用完就得换新的。这样设计也是比较正常的,对状态的要求比较严格,例如都开车了你再告诉司机,这次加了5个人,车都开了一半了,不会考虑再回去。随意修改约束值会带来很多逻辑上的问题。
CountDownLatch无限等待
countDown没有被调用,那么await就会一直等下去。countDown常见没有被调用的情况:异常中断,线程池拒绝策略。
可以使用
public boolean await(long timeout, TimeUnit unit)
根据业务情况,增加一个最大等待时间。使用这种方式,需要对失败的各种情况作出业务上的对应处理,否则就出现各种数据不正确的问题。也可以对countDown的线程做好异常处理,最好使用另外一个线程池来处理这些线程。这种情况就需要对业务不能停顿时间特别长,导致线程池的资源被耗光的情况做处理。如果是想通过new Thread避免,就需要考虑线程突然暴涨的问题。
CountDownLatch详解的更多相关文章
- 最强Java并发编程详解:知识点梳理,BAT面试题等
本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...
- AQS详解
一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...
- Java并发之AQS详解
一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...
- java并发包java.util.concurrent详解
线程池ThreadPoolExecutor的使用 并发容器之CopyOnWriteArrayList 并发容器之CopyOnWriteArraySet 数据结构之ConcurrentHashMap,区 ...
- (转)Java并发包基石-AQS详解
背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...
- 【1】AQS详解
概述: 它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点加入到同步队列尾部(采用自旋CAS来 ...
- Zookeeper客户端Curator使用详解
Zookeeper客户端Curator使用详解 前提 最近刚好用到了zookeeper,做了一个基于SpringBoot.Curator.Bootstrap写了一个可视化的Web应用: zookeep ...
- Java性能分析之线程栈详解与性能分析
Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...
- Java并发之AQS详解(转)
一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...
随机推荐
- HttpRunner 参数化数据驱动
HttpRunner 2.0 参数化数据驱动案例,废话不说,直接上干货. 1.测试用例目录结构 api:接口集 testcases:测试用例 testsuites:测试套件 data: ...
- PNPoly算法代码例子,判断一个点是否在多边形里面
写C语言的实验用到的一个算法,判断一个点是否在多边形的内部.C的代码如下: int pnpoly(int nvert, float *vertx, float *verty, float testx, ...
- 【开发者笔记】冒泡排序过程呈现之java内置GUI表示
自己玩玩写写,排序的过程多么有趣,特别是把看着电脑吧一堆乱七八糟的数据排成有序组合的时候,看起来贼舒服,特别是强迫症患者.好了,话不多说上代码,也算是自己记录一下吧,没有什么技术含量但个人感觉比较有趣 ...
- ActiveRecord多数据库配置
ActiveRecord 的多数据库配置基本沿袭了 NHibernate 的思想,只不过在配置文件结构上作了些调整.NHibernate的配置也是基于配置得来的,配置多个SessionFactory传 ...
- 数据结构 练习21-trie的原理分析和应用
前言 今天具体分析一下trie树,包括:原理分析,应用场合,复杂度分析,与hash的比较,源码展现.大部分内容来自互联网,文中会注明出处. 原理分析 主要是hash树的变种,先看下图: 每一个点存储一 ...
- PKU 2823 Sliding Window(线段树||RMQ||单调队列)
题目大意:原题链接(定长区间求最值) 给定长为n的数组,求出每k个数之间的最小/大值. 解法一:线段树 segtree节点存储区间的最小/大值 Query_min(int p,int l,int r, ...
- POJ - 2175 Evacuation Plan (最小费用流消圈)
题意:有N栋楼,每栋楼有\(val_i\)个人要避难,现在有M个避难所,每个避难所的容量为\(cap_i\),每个人从楼i到避难所j的话费是两者的曼哈顿距离.现在给出解决方案,问这个解决方案是否是花费 ...
- cocos2dx 3.x 精灵重叠时点击最上层的精灵
ps. 这个方法只适用设置精灵的触摸.. //注册触摸事件..3.X后可以在这样写..不需要重新声明 EventListenerTouchOneByOne *listener = EventListe ...
- RedisTemplate和StringRedisTemplate
最近在开始在学习Redis以及如何在Java当中去使用Redis,Redis是什么我这里就不说了. 我主要想说的是Redis和Java当中Spring结合起来的时候,使用到的RedisTemplate ...
- Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)返回非映射实体类的解决方法
Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)易错使用 一直用ORM,今天用JdbcTemplate ...