CountDownLatch源码浅析
Cmd Markdown链接
参考好文:
前言
CountDownLatch用于同步一个或者多个任务,强制他们等待有其他任务执行的一组操作完成。CountDownLatch典型的用法是将一个程序分成n个相互独立的可分解任务,并创建值为n个互相独立的可分解任务,并创建值为n的CountDownLatch。当每一个任务完成时,都会在调用countdown方法将count-1,等待问题被解决的任务调用这个锁的await,将自身拦截等待,直到锁计数为0,才继续往下执行。
前沿知识
CAS
CAS:Compare and Swap 可以翻译成比较并交换。
Excample:
CAS操作包含三个操作数 --- 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值想匹配,那么处理器会自动将该位置值更新为新值;否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。(在CAS的一些特殊情况下将仅返回CAS是否成功,而不提前当前值。)CAS有效地说明了”我认为位置V的值应该为值A,如果等于该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可“。
通常将CAS用于以同步的方式从地址V读取值A,执行多步计算来获得新值B,然后使用CAS将V的值从A改为B。如果V处的值尚未被更改,则CAS操作成功。
线程队列
CountDownLatch是为了实现公平锁,采用了队列的形式,保证先到先执行。
从构造函数CountDownLatch(int count)说起
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
说明: 构造函数创建了一个Sync对象,而Sync是继承于AQS类。Sync构造函数如下:
Sync(int count) {
setState(count);
}
protected final void setState(int newState) {
state = newState;
}
说明:上面代码中的state就是CountDownLatch的“锁计数器”,每次调用CountDownLatch的countdown()方法都是通过CAS的方式将state减1,直到state为0。
countDown()方法:
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
说明: countDown -> releaseShared -> tryReleaseShared的作用是释放占用的锁资源;从for(;;) {} 的形式,可以看出其内部是通过自旋的形式,一直尝试释放锁,直到当前锁状态为0或者释放成功。compareAndSetState(c, nextc) 可以看出其是通过CAS的方式保证原子性的释放锁。
private void doReleaseShared() {
for (;;) {
Node h = head;
//头节点不为null且不等于尾节点 即头节点是实际有意义的节点
if (h != null && h != tail) {
int ws = h.waitStatus;
// Node.SIGNAL:waitStatus value to indicate successor's thread needs unparking
// 即:当头节点的状态为-1时,表示需要唤醒后面的进程。
if (ws == Node.SIGNAL) {
// 通过CAS的形式对head的状态进行更新,更新失败就continue,直到成功。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h); // 唤醒后续的进程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
说明: doReleaseShared的作用主要是在释放共享锁成功后,通知后面的节点。
await()方法
如果这里多个线程wait且当前共享锁还不为0则这些wait线程的状态如下图:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted()) // 判断线程的状态,如果线程已经状态 就抛出异常
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
说明: getState()返回的是当前CountDownLatch对象剩余的共享锁数量。从tryAcquireShared方法的返回值可以看出:当共享锁的数量不为0时,执行doAcquireSharedInterruptibly(arg),即当前调用await的线程需要加入到等待共享锁释放的队列而不是可以立即往下执行时,进入到doAcquireSharedInterruptibly(arg)方法进行等待。
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
//由于采用了公平锁,所以要将节点放到队列里,保证先到先执行。
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) { //开启自旋模式 一直等待,直到锁被全部释放。
final Node p = node.predecessor();//获取当前节点的前继节点(node.prev)
// 如果当前节点的前继节点(node.prev)等于head 头节点,则表示当前节点是等待队列中
// 的第一个节点。满足 先到先执行的 公平顺序,则尝试释放锁。
if (p == head) {
//再次获取 当前剩余的共享锁数量是否为0,如果为0,则表示可以进行后续的唤醒动作
int r = tryAcquireShared(arg); // (getState() == 0) ? 1 : -1;
if (r >= 0) { // 满足唤醒的条件 开始进行唤醒动作
// 将当前的node设置为head,也就是模拟当前node出队列动作
// 同时唤醒当前出队列的node节点。
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//校验线程是否被打断,如果被打断则抛出异常
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
说明:
- 当有多个线程执行await()方法,且共享锁的数量尚未等于0的情况下,等待队列如下图(0先到 1、2、3依次)

doAcquireSharedInterruptibly方法的执行逻辑如下图,特别要说明的是Thread0 Thread1 Thread2 Thread3内部都并行在执行下图逻辑,不断校验自己的前驱节点是否为head,自身是否为中断。
CountDownLatch源码浅析的更多相关文章
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- Struts2源码浅析-ConfigurationProvider
ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...
- (转)【深入浅出jQuery】源码浅析2--奇技淫巧
[深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html
- HashSet其实就那么一回事儿之源码浅析
上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap, 本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...
- Android 手势识别类 ( 三 ) GestureDetector 源码浅析
前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...
- Android开发之Theme、Style探索及源码浅析
1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...
- 【深入浅出jQuery】源码浅析2--使用技巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- Android手势源码浅析-----手势绘制(GestureOverlayView)
Android手势源码浅析-----手势绘制(GestureOverlayView)
随机推荐
- 使用javaScript和JQuery制作经典面试题:光棒效果
使用javaScript与jQuery添加CSS样式的区别和步骤 使用javaScript制作光棒效果 --首先是javaScript <script> $(function () { v ...
- 从MYSQL数据库查出指定格式的日期
1.用SQL语言控制: 格式如下: select DATE_FORMAT(t.startTime,"%Y-%m-%d %H:%i") AS startTime, DATE_FORM ...
- STL之三:deque用法详解
转载于:http://blog.csdn.net/longshengguoji/article/details/8519812 deque函数: deque容器为一个给定类型的元素进行线性处理,像向量 ...
- HTTP的特点?
(1)HTTP是无连接: 无连接的含义是限制每次连接只处理一个请求.服务器处理完客户的请求,并收到客户的应答后,即断开连接.采用这种方式可以节省传输时间. (2)HTTP是媒体独立的: 这意味着,只要 ...
- jQuery无法获取隐藏元素(display:none)宽度(width)和高度(height)的新解决方案
用jQuery写一个通过点击左右图标来翻阅图片的小插件,写好后测试可以正常运行,但是放到Tab中后发现只有第一个Tab中的代码能够正常运行,其它全部罢工了. 用Chrome自带的开发工具一查,发现罢工 ...
- C11简洁之道:类型推导
1. 概述 C++11里面引入了auto和decltype关键字来实现类型推导,通过这两个关键字不仅能方便的获取复杂的类型,还能简化书写,提高编码效率. 2. auto 2.1 auto关键字的新 ...
- 快速排序Quick sort
快速排序Quick sort 原理,通过一趟扫描将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归 ...
- PHP 练习1:新闻发布
1.新闻发布主页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...
- Creating a new dynamic form project, business modeling.
The domain logic is like there are a bunch of objects, as well as a lot of configurations, according ...
- SQL Server Delete Duplicate Rows
There can be two types of duplication of rows in a table 1. Entire row getting duplicated because th ...