在过去我们实现多线程同步的代码中,往往使用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. FZU Problem 2062 Suneast &amp; Yayamao

    http://acm.fzu.edu.cn/problem.php?pid=2062 标题效果: 给你一个数n,要求求出用多少个数字能够表示1~n的全部数. 思路: 分解为二进制. 对于一个数n.看它 ...

  2. IdentityServer4实战 - 谈谈 JWT Token 的安全策略

    原文:IdentityServer4实战 - 谈谈 JWT Token 的安全策略 一.前言 众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference To ...

  3. 【struts2+hibernate4】小型电子商务站点

    这里使用的是struts2和hibernate4两个框架开发的一个小型电子商务站点,数据库方面我也会给出对应的代码. 总之使用的是:struts2+hibernate4+jsp+MySQL+tomca ...

  4. NYOJ - 括号匹配(二)(经典dp)

    括号匹配(二) 时间限制:1000 ms | 内存限制:65535 KB 难度:6 描写叙述 给你一个字符串,里面仅仅包括"(",")","[&quo ...

  5. 整了一天,明白一个道理:线程里post数据,即loop.exec+quit,然而这个quit之后,导致无法在线程里建立新的loop.exec,直接就退出了

    跟踪到exec的代码里,发现: 无奈,把第二个post移到主线程里去执行了. 如果大家发现有好办法,请告知我.

  6. 机器学习: TensorFlow with MLP 笑脸识别

    Tensor Flow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库.节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数 ...

  7. Binding的三种方式

    1 Text="{Binding Name}" Name为后台的属性 2 Text="{Binding ElementName=XXX,Path=A.B.C.D….}&q ...

  8. 漫谈 JVM —— 内存模型、线程、锁

    Java 内存模型(JMM),实际上的目的就是为了统一内存管理.这让我想到了,作为一个程序员总是想着有银弹,有一个代码能万能的在所有场景上.经过多次尝试我发现这是不可能的:需求在变,技术在更新,没有什 ...

  9. spring-mvc+freemarker整合(sonne_game网站开发03)

    今天的任务就是在spring+mybatis+springmvc的基础上,将freemarker整合进来. freemarker是什么? freemarker是一种模板引擎.它的目的是基于模板和数据, ...

  10. MIPS开发板的“不二”选择——Creator Ci20单板计算机评测(芯片是君正JZ4780 ,也就是MIPS R3000,系统推荐Debian或深度,官网就有,其它语言有FreePascal和Go和Java和Python)

    在MIPS架构的CPU上开发软件,当然需要使用MIPS专用的工具链来编译代码.不过一般的LINUX发行版内都有相应的配套工具链供用户使用.Ci20出厂时的LINUX发行版为DEBIAN 7.5,相应的 ...