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. C/C++常用编辑器

    VIM ,www.vim.org/ Emacs, www.gnu.org/software/emacs/ notepad++,www.notepad-plus-plus.org/ Textmate,h ...

  2. 小鱼提问1 类中嵌套public修饰的枚举,外部访问的时候却只能Class.Enum这样访问,这是为何?

    /// <summary> /// 常量等定义 /// </summary> public class General { /// <summary> /// 文件 ...

  3. R包——ggplot2(一)

    关于ggplot2包(一) 关于ggplot2包(一) ggplot2基本要素 数据(Data)和映射(Mapping) 几何对象(Geometric) 标尺(Scale) 统计变换(Statisti ...

  4. java之观察者模式

    import java.util.Observable; import java.util.Observer; class House extends Observable {     private ...

  5. 64bits Python2.7.5安装numpy包

    由于数值分析需要numpy计算包,我找了很多numpy-cp27的下载地址,下了最新版的.whl文件,但总是安装不成功,后来找到一个.exe文件 直接下载安装即可使用:下面是网址链接http://do ...

  6. MySQL、You are using safe update mode

    MySQL默认模式是sql_safe_updates=1的.在这个模式下不管你是update还是delete一行where 条件都要指定主键.如果where条件只出现的不是主键就会出错. 例子: se ...

  7. 投资新兴市场和细分市场 good

    新兴市场对程序员来说,就是一种新的语言.一个新的平台.一套新的框架.新兴市场因为刚刚兴起,所以几乎所有人都在同一个起跑线,特别适合后进者.我认识从一个2011年开始学习iOS开发的同学,他能能力中等, ...

  8. Socket编程指南及示例程序

    例子代码就在我的博客中,包括六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具工程,和此小工具的所有运行时库.资源和执行程序.代码的压缩包位置是http://www.blogja ...

  9. Windows下如何建立以"."开头的文件夹

    Windows资源管理器不允许创建点开头的文件或文件夹,但在cmd命令提示符下是可以的: 创建命令: md d:\.myfolder .myfolder就是以点开头的文件夹的名称

  10. Is it possible to implement a Firebug-like “inspect element” DOM element highlighter with client-side JavaScript?

    Is it possible to implement a Firebug-like "inspect element" DOM element highlighter with ...