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. BZOJ 1407: [Noi2002]Savage( 数论 )

    枚举答案, 然后O(N^2)枚举野人去判他们是否会在有生之年存在同山洞. 具体做法就是: 设第x年相遇, 则 Ci+x*Pi=Cj+x*Pj (mod M), 然后解同余方程. 复杂度应该是O(ans ...

  2. ROS中编辑文件命令行工具rosed

    rosed是rosbash套件中的一个,它允许我们通过包名直接编辑包中的文件,而不是输入包的全部路径. 用法: rosed [package_name] [filename] 例如: rosed ro ...

  3. C学习之结构体

    结构体(struct) 结构体是由基本数据类型构成的.并用一个标识符来命名的各种变量的组合,结构体中可以使用不同的数据类型. 1. 结构体说明和结构体变量定义 在Turbo C中, 结构体也是一种数据 ...

  4. mina、netty消息边界问题(采用换行符)

    在TCP连接开始到结束连接,之间可能会多次传输数据,也就是服务器和客户端之间可能会在连接过程中互相传输多条消息.理想状况是一方每发送一条消息,另一方就立即接收到一条,也就是一次write对应一次rea ...

  5. Linux各个发行版本的介绍, 以及VirtualBox+CentOS的安装步骤

    Linux和Unix系统有哪些主要的发行版本 Unix: (非开源传统商业操作系统) IBM AIX, HP HP-UX, Sun Solaris,等 各家硬件厂商的发行版本, 往往是和自家的硬件设备 ...

  6. php功能---删除空目录

    header('content-type:text/html;charset:utf-8'); function display($dir){ //判断是否是一个目录 if(!is_dir($dir) ...

  7. uglifyjs note

    uglifyjs UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit. uglifyjs是用js写的 ...

  8. Oracle的TPCC测试,原来也是个作弊的东西...

    http://www.oaktable.net/content/sorted-hash-clusters-rip 根据Jonathan Lewis老先生的测试实例,发觉cluster 的sort功能, ...

  9. <td style="word-break:break-all"> 在html中控制自动换行

    在html中控制自动换行   其实只要在表格控制中添加一句 <td style="word-break:break-all">就搞定了. 其中可能对英文换行可能会分开一 ...

  10. Head First设计模式学习笔记

    最近在学C++,直接语法之后觉得不太有意思,直接做项目又觉得太肤浅.正好之前一直想学设计模式来着,可惜之前一直在玩C,所以没有机会深入学习,于是决定用C++把设计写一遍.看了点GOF的<设计模式 ...