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-实例、源码和模拟实现的更多相关文章

  1. [置顶] Android 高级开发 源码 UI 缓存 网络

    1.Android 源码剖析 性能优化  开源代码 2.Android UI效果源码 3.http://mzh3344258.blog.51cto.com/1823534/d-3 4.微信公众平台开发 ...

  2. 45个android实例源码

    分享45个android实例源码,很好很强大http://www.apkbus.com/android-20978-1-1.html andriod闹钟源代码http://www.apkbus.com ...

  3. 分享45个android实例源码,很好很强大

    分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...

  4. JAVA上百实例源码以及开源项目

    简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...

  5. 百度地图 api 功能封装类 (ZMap.js) 本地搜索,范围查找实例 [源码下载]

    相关说明 1. 界面查看: 吐槽贴:百度地图 api 封装 的实用功能 [源码下载] 2. 功能说明: 百度地图整合功能分享修正版[ZMap.js] 实例源码! ZMap.js 本类方法功能大多使用 ...

  6. 百度地图整合功能分享修正版[ZMap.js] 实例源码!

    ZMap 功能说明 ZMap 是学习百度地图 api 接口,开发基本功能后整的一个脚本类,本类方法功能大多使用 prototype 原型 实现: 包含的功能有:轨迹回放,圈画区域可编辑,判断几个坐标是 ...

  7. EasyUI实例源码

    jQuery+EasyUI实例源码 http://www.51aspx.com/code/jQueryEasyUIExample ASP.NET MVC+EF+EasyUI权限 http://www. ...

  8. 微信小程序实例源码大全

    微信小程序实例源码大全下载 微信小应用示例代码(phodal/weapp-quick)源码链接:https://github.com/phodal/weapp-quick 微信小应用地图定位demo( ...

  9. robotlegs2.0框架实例源码带注释

    robotlegs2.0框架实例源码带注释 Robotlegs2的Starling扩展 有个老外写了robotleges2的starling扩展,地址是 https://github.com/brea ...

随机推荐

  1. 解决一个Android Studio gradle的小问题

    自从Android Studio有了gradle之后,就经常有问题,最近在Ubuntu上用Android Studio的时候就遇到一个问题,每次项目目录更改了,Import项目,打开项目,还是新建项目 ...

  2. PHP判断是中文还是英文

    static function ischinese($s){ $allen = preg_match("/^[^/x80-/xff]+$/", $s); //判断是否是英文 $al ...

  3. 《Pointers On C》读书笔记(第四章 语句)

    1.空语句只包含一个分号,它本身并不执行任何任务,其适用的场合是语法要求出现一条完整的语句,但并不需要它执行任何任务. 2.C语言中并不存在专门的“赋值语句”,赋值就是一种操作,在表达式内进行.通过在 ...

  4. delphi 7 信息对话框的按钮屏蔽键盘操作,只允许鼠标点击

    本问题由 delphi 学友QQ群中一位群友提出,个人觉得是一个好问题. 本教程源码下载 本教程面向新手,希望大家能从中学到除了本功能之外的真正编程技能. 本功能的实现原理,用窗口当对话框的界面,在界 ...

  5. 源码安装rsyslog

    <pre name="code" class="html">下载下列软件 json-c-0.12-20140410.tar.gz---------- ...

  6. Azure SQL 数据库引入了新的服务级别

     新的级别将提升客户体验,并提供更多的业务连续性选项 为了更好地满足您在灵活性提升方面的需求,MicrosoftAzure SQL 数据库添加了新的服务级别(基础级和标准级),以与当前处于预览状态 ...

  7. 优步uber司机怎么注册不了?注册优步司机问题要点

    第一,可能是你的车型不符全要求,看是不是5年内的车型,同时要求车价8W以上:第二,你的驾驶年限不够,要求驾驶证年限1年以上的,如果不够的怎么办,告诉你个方法,PS啊!优步可查不了车管所的系统,所以这类 ...

  8. 吃透C#集合~大话目录

    最近买了一本<C#数据结构>的书,这种书确实少见,一般的数据结构都是采用C,C++来实现的,C#可以说是稀有了,呵呵,书写的不错,把C#的核心Collections介绍了一个透彻,对于我来 ...

  9. 设置android:supportsRtl=&quot;true&quot;无效问题

     今天解bug时,遇到这样一个问题:   问题描写叙述:切换系统语言为阿拉伯文时,actionbar布局没有变为从右向左排列.   于是,我在Androidmanifest.xml文件里的 appli ...

  10. [原创] ASP.NET WEBAPI 接入微信公众平台 总结,Token验证失败解决办法

    首先,请允许我说一句:shit! 因为这个问题不难,但是网上有关 ASP.NET WEBAPI的资料太少.都是PHP等等的. 我也是在看了某位大神的博客后有启发,一点点研究出来的. 来看正题! 1.微 ...