在过去我们实现多线程同步的代码中,往往使用join()、wait()、notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实现多线程的同步,今天我们就对其中CountDownLatch类的使用与底层实现进行分析与总结。

一、CountDownLatch使用

CountDownLatch其实可以看做一个计数器,统计多个线程执行完成的情况,适用于控制一个或多个线程等待,直到所有线程都执行完毕的场景,类似与Thread.join()的作用。下面我们通过一个简单的例子看下CountDownLatch的使用。

    public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5); // 启动计数线程
for (int i = 0; i < 5; i++) {
new CountDownLatchThread(i, countDownLatch).start();
} // 启动等待线程
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() { try {
countDownLatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println("计数完毕了," + Thread.currentThread().getName() + "等待线程执行"); }
}.start();
} }

计数线程代码:

public class CountDownLatchThread extends Thread {

    private CountDownLatch countDownLatch;

    private int name;

    private int count;

    public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
this.name = name;
this.countDownLatch = countDownLatch;
this.count = 0;
} public void run() {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
count++;
}
System.out.println(name + "号线程--" + Thread.currentThread().getName() + "--计数完成了");
countDownLatch.countDown(); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

输出结果:

1号线程--Thread-1--计数完成了
0号线程--Thread-0--计数完成了
4号线程--Thread-4--计数完成了
2号线程--Thread-2--计数完成了
3号线程--Thread-3--计数完成了
计数完毕了,Thread-5等待线程执行
计数完毕了,Thread-6等待线程执行
计数完毕了,Thread-7等待线程执行
计数完毕了,Thread-8等待线程执行
计数完毕了,Thread-9等待线程执行

通过上面的例子可以看到,利用CountDownLatch的countDown方法与await()方法,我们可以同步计数线程与等待线程,使等待线程在所有计数线程完成后再开始运行。

二、CountDownLatch源码分析

接下来我们对countDownLatch内部源码进行一下分析。

1、CountDownLatch的构造。

首先看下CountDownLatch的构造函数

    public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}

CountDownLatch的构造函数会接收一个count值做为计数器,也就是如果你需要等待N个线程执行结束,那这里就传入N。同时CountDownLatch会实例化一个Sync对象,这个Sync其实是CountDownLatch内部定义的一个继承自AbstractQueuedSynchronizer的实现类,所以CountDownLatch提供的同步和其他功能都是围绕Sync这个子类实现的,也就是基于AbstractQueuedSynchronizer类来实现的。

我们来看下Sync这个类的定义

    private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L; /**
1、设置AbstractQueuedSynchronizer中同步状态的值state,也就是计数器的值。
2、这个值volatile变量,必须保证线程间的可见性;
**/
Sync(int count) {
setState(count);
} int getCount() {
return getState();
} //获取同步状态的值
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
} protected boolean tryReleaseShared(int releases) {
// 通过CAS操作改变同步状态值,保证同步状态的值state的线程安全。
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}

2、countDown方法

首先我们看下countDown方法的源码

    public void countDown() {
//改变同步状态值,线程执行完成时计数器减一
sync.releaseShared(1);
}

AbstractQueuedSynchronizer类中releaseShared() 方法的源码

    public final boolean releaseShared(int arg) {
// CountDownLatch定义的子类Sync实现,通过CAS操作改变State的值
if (tryReleaseShared(arg)) {
//State以递减为0,代表着所有执行线程执行完毕,共享模式下释放锁,那么等待线程就能够拿到锁往下执行。
doReleaseShared();
return true;
}
return false;
}

当调用CountDownLatch的countDown方法时,就会执行计数器进行减一操作,直到所有线程全部执行完毕,计算器为0时唤醒等待线程。

AbstractQueuedSynchronizer中doReleaseShared方法是执行共享模式下释放锁的操作,从而让等待线程获取锁,继续向下执行。

3、await方法

    public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}

AbstractQueuedSynchronizer类中acquireSharedInterruptibly() 方法的源码

    public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//获取同步状态值
if (tryAcquireShared(arg) < 0)
//同步状态值即计数器的值不为0,等待线程共享模式下尝试获取锁,获取不到锁的话进入阻塞
doAcquireSharedInterruptibly(arg);
}

await方法的实现也很明确,首页获取同步状态也就是计数器的值,如果为0即所有线程执行完毕返回1,否则返回-1的话,等待线程在共享模式下尝试获取锁,获取不到锁的话进入阻塞;

AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是执行共享模式下获取锁的操作;

三、总结

通过上面分析可以看到CountDownLatch是基于AbstractQueuedSynchronizer类实现的,一个非常实用的多线程控制工具类,它类似与一个计数器用来控制指定的线程等待,直到计数器归零。以上我们对CountDownLatch类的使用与核心方法的源码进行了一定的分析,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

Java多线程同步工具类之CountDownLatch的更多相关文章

  1. Java多线程同步工具类之Semaphore

    Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...

  2. Java多线程同步工具类之CyclicBarrier

    一.CyclicBarrier使用 CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会 ...

  3. Java多线程并发工具类-信号量Semaphore对象讲解

    Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...

  4. 【同步工具类】CountDownLatch闭锁任务同步

    [同步工具类]CountDownLatch闭锁任务同步 转载:https://www.cnblogs.com/yangchongxing/p/9214284.html 打过dota的同学都知道,多人一 ...

  5. java 利用同步工具类控制线程

    前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...

  6. 【同步工具类】CountDownLatch

    闭锁是一种同步工具类,可以延迟线程的进度直到其达到终止状态. 作用:相当于一扇门,在到达结束状态之前,这扇门一直是关闭的,并且没有任务线程能够通过,当到达结束状态时,这扇门会打开并允许所有的线程通过, ...

  7. 深入分析同步工具类之CountDownLatch

    概览: CountDownLatch又称闭锁,其作用是让一个或者多个线程挂起,直到其他的线程执行完后恢复挂起的线程,使其继续执行.内部维护着一个静态内部类Sync,该类继承AbstractQueued ...

  8. Java多线程并发工具类

    Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问 ...

  9. Java并发——同步工具类

    CountDownLatch  同步倒数计数器 CountDownLatch是一个同步倒数计数器.CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatch对象 ...

随机推荐

  1. 面试问题:Vista与XP的Session 0与Session X的区别

    面试问题:Vista与XP的Session 0与Session X的区别 在XXXXX的一次面试中,笔试问题的题目曾提到Session 0.Session 1在Vista和Xp中的区别?现在把答案发上 ...

  2. android模拟器上不了网的解决办法

    Android模拟器默认的地址是10.0.2.3,默认的DNS也是10.0.2.3,对于在家里上网学习Android的人来讲,一般电脑的IP都是192.168.1.100之类的,不在同一个网段.所以就 ...

  3. QT之二级菜单(二级菜单的箭头可以使用QSS设置图片)

    QT之二级菜单 QT之二级菜单 开场白 效果图 上代码 可参考文章 下代码 结尾 开场白 今天我们一起来了解下,在我们QT中,二级菜单是如何实现的,在上篇我们学习了QT之系统托盘,QT之自定义菜单,  ...

  4. SpringMVC+easyUI 分页,查询 (完整的CRUD)

    最终完毕CRUD的功能了,注意,这里会对前面有一些修改,UserController的listUser() 已经改写了,如今把所有整理一下吧. JSP: <%@ page language=&q ...

  5. EF codefirst第一篇

    一直以来喜欢dbfirst  因为简单,一直不明白为什么codefirst会是主流,根据对ddd的学习终于知道了codefirst的目的 本文是对博客园 小崔的笔记本 文章 EF实体框架之CodeFi ...

  6. 回调函数实现类似QT中信号机制

    1. 定义回调接口类: class UIcallBack { public: virtual void onAppActivated() = 0; virtual void onShowMore()  ...

  7. 零元学Expression Design 4 - Chapter 6 教你如何在5分钟内做出文字立体感效果

    原文:零元学Expression Design 4 - Chapter 6 教你如何在5分钟内做出文字立体感效果 又来一篇五分钟做设计啦~ 本篇将教大家如何运用Design内建工具Blend Path ...

  8. ZOJ 2319 Beatuiful People(单调递增序列的变形)

    Beautiful People Time Limit: 5 Seconds      Memory Limit: 32768 KB      Special Judge The most prest ...

  9. 正定矩阵(definite matrix)

    1. 基本定义 在线性规划中,一个对称的 n×n 的实值矩阵 M,如果满足对于任意的非零列向量 z,都有 zTMz>0. 更一般地,对于 n×n 的 Hermitian 矩阵(原矩阵=共轭转置, ...

  10. 用callgraph生成的函数调用关系图

    Wu Zhangjin 创作于 2015/04/05 评论打赏 By Falcon of TinyLab.org 2015/04/03 1 故事缘由 源码分析是程序员离不开的话题.无论是研究开源项目, ...