[置顶] JDK-CountDownLatch-实例、源码和模拟实现
Conception
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
根据CountDownLatch的document,就是说CountDownLatch作为一个同步的助手,可以阻塞一个线程,等待另一个线程结束了再执行。
所以CountDownLatch的作用在于:通过阻塞来协调线程间的执行顺序。
CountDownLatch最重要的方法为await()和countDown()。首先,初始化指定一个count,每次countDown()都会count--,而await()就是阻塞调用它的方法,直到count==0。
所以可以把CountDownLatch看成同步的计数器,什么时候倒数到0就执行,否则阻塞。
Demo
我们首先做一个CountDownLatch的demo,看看它是怎么一个阻塞。
count初始化为2,所以count.await()会阻塞主线程,直到两个processor都countDown(),即count==0的时候才会执行,打印出"Processors has been done."
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(2);
ExecutorService exc = Executors.newSingleThreadExecutor();
System.out.println("Before submit the processor.");
exc.submit(new CountProcessor(count, "P1"));
exc.submit(new CountProcessor(count, "P2"));
System.out.println("Has submited the processor.");
count.await();
System.out.println("Processors has been done.");
exc.shutdown();
}
}
class CountProcessor extends Thread {
private CountDownLatch count;
private String name;
public CountProcessor(CountDownLatch count, String name) {
this.count = count;
this.name = name;
}
@Override
public void run() {
try {
Thread.currentThread().sleep(1000);
System.out.println(this.name + " has been done.");
count.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
修改一下上面的程序,我们可以模拟一个百米赛跑的现场:
首先运动员进场:前期数据准备和线程池的初始化。
准备工作完成后统一起跑:传入的begin.await()一直阻塞着Runner.run(),在前期准备完成后,begin.countDown()后begin.count==0,阻塞结束,Runner起跑。
比赛一直进行,直到所有选手冲过终点:end.await()会阻塞主线程,所以最后的那条print语句不会提前打印,而是等待Runner.run()结束执行end.countDown(),减少3次直到end.count==0才接触阻塞,宣布比赛结束。
这就是CountDownLatch的经典场景,统一开始,统一结束。
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(3);
ExecutorService exc = Executors.newCachedThreadPool();
System.out.println("Runners are comming.");
exc.submit(new CountProcessor(begin, end, "Runner_1", 1000));
exc.submit(new CountProcessor(begin, end, "Runner_2", 2000));
exc.submit(new CountProcessor(begin, end, "Runner_3", 3000));
System.out.println("Ready.");
Thread.currentThread().sleep(1000);
System.out.println("Go.");
begin.countDown();
end.await();
System.out.println("All runners Finish the match.");
exc.shutdown();
}
}
class CountProcessor extends Thread {
private CountDownLatch beginCount;
private CountDownLatch endCount;
private String name;
private int runningTime;
public CountProcessor(CountDownLatch beginCount, CountDownLatch endCount, String name, int runningTime) {
this.beginCount = beginCount;
this.endCount = endCount;
this.name = name;
this.runningTime = runningTime;
}
@Override
public void run() {
try {
this.beginCount.await();
System.out.println(this.name + " start.");
Thread.currentThread().sleep(this.runningTime);
System.out.println(this.name + " breast the tape.");
this.endCount.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Source Code
我们来看JDK中CountDownLatch是怎么定义的,以及其中的主要方法。
public class CountDownLatch {
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
}
很显然,CountDownLatch的逻辑都封装在内部类Sync中,这也是通过装饰者模式来提高封装性的普遍做法。
Sync继承了AbstractQueuedSynchronizer,这里的调用关系有点麻烦,我就不一一展开了,我贴了部分关键的代码(如下)。
概括一下,CountDownLatch的count存放在以volatile声明的变量state。在await()的时候for轮询state,一直轮询以达到阻塞的效果,直到state==0。而countDown()就是通过CompareAndSet的方式来递减state。
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
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;
}
}
}
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
private volatile int state;
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
}
Simulator
从上面可以看出来,其实CountDownLatch的实现很简单,countDown的时候原子修改,await的时候循环判断volatile来阻塞。
所以完全可以通过原子类来实现,而且还更加beautiful。以下,我用AtomicInteger来模拟CountDownLatch的实现。
测试程序还是和上面的赛跑程序一样的,只是把CountDownLatch替换成了CountDownLatchSimulator。
最后测试结果还是一样的,只是过程简单了许多,因为JDK把很多防止冲突的逻辑都封装在原子类了。其中await的时候,可以在循环中加上sleep,能减低系统轮询的消耗。
【
我只是做了一个简单的实现,至于性能和安全性方面,还希望大神们指导
】
public class CountDownLatchSimulator {
private AtomicInteger count;
public CountDownLatchSimulator(int count) {
this.count = new AtomicInteger(count);
}
public void await() throws InterruptedException {
while (this.count.get() != 0) {
// Thread.currentThread().sleep(100);
}
}
public void countDown() {
this.count.getAndDecrement();
}
public static void main(String[] args) throws InterruptedException {
CountDownLatchSimulator begin = new CountDownLatchSimulator(1);
CountDownLatchSimulator end = new CountDownLatchSimulator(3);
ExecutorService exc = Executors.newCachedThreadPool();
System.out.println("Runners are comming.");
exc.submit(new CountProcessor2(begin, end, "Runner_1", 1000));
exc.submit(new CountProcessor2(begin, end, "Runner_2", 2000));
exc.submit(new CountProcessor2(begin, end, "Runner_3", 3000));
System.out.println("Ready.");
Thread.currentThread().sleep(2000);
System.out.println("Go.");
begin.countDown();
end.await();
System.out.println("All runners Finish the match.");
exc.shutdown();
}
}
class CountProcessor2 extends Thread {
private CountDownLatchSimulator beginCount;
private CountDownLatchSimulator endCount;
private String name;
private int runningTime;
public CountProcessor2(CountDownLatchSimulator beginCount, CountDownLatchSimulator endCount, String name, int runningTime) {
this.beginCount = beginCount;
this.endCount = endCount;
this.name = name;
this.runningTime = runningTime;
}
@Override
public void run() {
try {
this.beginCount.await();
System.out.println(this.name + " start.");
Thread.currentThread().sleep(this.runningTime);
System.out.println(this.name + " breast the tape.");
this.endCount.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[置顶] JDK-CountDownLatch-实例、源码和模拟实现的更多相关文章
- [置顶] Android 高级开发 源码 UI 缓存 网络
1.Android 源码剖析 性能优化 开源代码 2.Android UI效果源码 3.http://mzh3344258.blog.51cto.com/1823534/d-3 4.微信公众平台开发 ...
- 45个android实例源码
分享45个android实例源码,很好很强大http://www.apkbus.com/android-20978-1-1.html andriod闹钟源代码http://www.apkbus.com ...
- 分享45个android实例源码,很好很强大
分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...
- JAVA上百实例源码以及开源项目
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...
- 百度地图 api 功能封装类 (ZMap.js) 本地搜索,范围查找实例 [源码下载]
相关说明 1. 界面查看: 吐槽贴:百度地图 api 封装 的实用功能 [源码下载] 2. 功能说明: 百度地图整合功能分享修正版[ZMap.js] 实例源码! ZMap.js 本类方法功能大多使用 ...
- 百度地图整合功能分享修正版[ZMap.js] 实例源码!
ZMap 功能说明 ZMap 是学习百度地图 api 接口,开发基本功能后整的一个脚本类,本类方法功能大多使用 prototype 原型 实现: 包含的功能有:轨迹回放,圈画区域可编辑,判断几个坐标是 ...
- EasyUI实例源码
jQuery+EasyUI实例源码 http://www.51aspx.com/code/jQueryEasyUIExample ASP.NET MVC+EF+EasyUI权限 http://www. ...
- 微信小程序实例源码大全
微信小程序实例源码大全下载 微信小应用示例代码(phodal/weapp-quick)源码链接:https://github.com/phodal/weapp-quick 微信小应用地图定位demo( ...
- robotlegs2.0框架实例源码带注释
robotlegs2.0框架实例源码带注释 Robotlegs2的Starling扩展 有个老外写了robotleges2的starling扩展,地址是 https://github.com/brea ...
随机推荐
- hadoop之hdfs学习
简介 HDFS(Hadoop Distributed File System )Hadoop分布式文件系统.是根据google发表的论文翻版的.论文为GFS(Google File System)Go ...
- c#Ulong用一个高位Uint和低位Uint表示
有时候考虑到平台之间的通用性,可能把一个Ulong拆分成2个Uint来进行各平台之间的通讯,当时转换的时候有点头晕,对与或预算不是很熟悉,不过还是花了半小时弄出来了,代码: //ulong的最大值2^ ...
- hdu 2295 Radar 重复覆盖+二分
题目链接 给m个雷达, n个城市, 以及每个城市的坐标, m个雷达里只能使用k个, 在k个雷达包围所有城市的前提下, 求最小半径. 先求出每个雷达到所有城市的距离, 然后二分半径, 如果距离小于二分的 ...
- POSIX扩展正则表达式函数
1.ereg()函数和eregi()函数 函数语法: bool ereg/eregi ( string pattern, string string [, array regs] ) 函数功能: 在字 ...
- Initialization failed for block pool Block pool(转载)
2014-06-18 20:34:59,622 FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed ...
- java 循环制作三角形
package hello; public class Sanjiao { public static void main(String[]args){ for(int i=1;i<5;i++) ...
- 使用ssh无密码登录
使用ssh无密码登录 ssh 是一个专为远程登录会话和其他网络服务提供安全性的协议.默认状态下ssh链接是需要密码认证的,可以通过添加系统认证(即公钥-私钥)的修改,修改后系统间切换可以避免密码输入和 ...
- 从头開始写项目Makefile(七):统一目标输出文件夹
[版权声明:转载请保留出处:blog.csdn.net/gentleliu. Mail:shallnew at 163 dot com] 上一节我们把规则单独提取出来,方便了Makefile的 ...
- Swift和Objective-C混合编程
假设你现在就是一个iOS程序员,你对Objective-C很熟悉,对iOS开发也很熟悉,然而,苹果公司在iOS 8之后推出了Swift语言.那么,如何才能快速地从Objective-C过渡到Swift ...
- Android应用开发提高篇(4)-----Socket编程(多线程、双向通信)
链接地址:http://www.cnblogs.com/lknlfy/archive/2012/03/04/2379628.html 一.概述 关于Socket编程的基本方法在基础篇里已经讲过,今天把 ...