Java多线程同步工具类之CountDownLatch
在过去我们实现多线程同步的代码中,往往使用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的更多相关文章
- Java多线程同步工具类之Semaphore
Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...
- Java多线程同步工具类之CyclicBarrier
一.CyclicBarrier使用 CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会 ...
- Java多线程并发工具类-信号量Semaphore对象讲解
Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...
- 【同步工具类】CountDownLatch闭锁任务同步
[同步工具类]CountDownLatch闭锁任务同步 转载:https://www.cnblogs.com/yangchongxing/p/9214284.html 打过dota的同学都知道,多人一 ...
- java 利用同步工具类控制线程
前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...
- 【同步工具类】CountDownLatch
闭锁是一种同步工具类,可以延迟线程的进度直到其达到终止状态. 作用:相当于一扇门,在到达结束状态之前,这扇门一直是关闭的,并且没有任务线程能够通过,当到达结束状态时,这扇门会打开并允许所有的线程通过, ...
- 深入分析同步工具类之CountDownLatch
概览: CountDownLatch又称闭锁,其作用是让一个或者多个线程挂起,直到其他的线程执行完后恢复挂起的线程,使其继续执行.内部维护着一个静态内部类Sync,该类继承AbstractQueued ...
- Java多线程并发工具类
Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问 ...
- Java并发——同步工具类
CountDownLatch 同步倒数计数器 CountDownLatch是一个同步倒数计数器.CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatch对象 ...
随机推荐
- UML类图几种”关系“的总结
在UML类图中,常见的有以下几种关系: 泛化(Generalization)(继承), 实现(Realization)(接口实现),组合(Composition),聚合(Aggregation),关 ...
- Massively parallel supercomputer
A novel massively parallel supercomputer of hundreds of teraOPS-scale includes node architectures ba ...
- MapReduce 编程 系列七 MapReduce程序日志查看
首先,假设须要打印日志,不须要用log4j这些东西,直接用System.out.println就可以,这些输出到stdout的日志信息能够在jobtracker网站终于找到. 其次,假设在main函数 ...
- ListView与GridView优化
前言 ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题.本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流 ...
- [Example of Sklearn] - 分类对比
refrence :http://cloga.info/python/2014/02/07/classify_use_Sklearn/ 加载数据集 这里我使用pandas来加载数据集,数据集采用kag ...
- 制作简单的WPF时钟
原文:制作简单的WPF时钟 在很早之前,我曾经写过一个GDI+的时钟,见"C#时钟控件 (C# Clock Control)" http://blog.csdn.net/johns ...
- 机器学习: DeepDreaming with TensorFlow (一)
在TensorFlow 的官网上,有一个很有趣的教程,就是用 TensorFlow 以及训练好的深度卷积神经(GoogleNet)网络去生成一些有趣的pattern,通过这些pattern,可以更加深 ...
- Paxos—以选美比赛为例PPT
- 基于Linux C的socketEthereal程序和Package分析 (一个)
执行测试平台:CentOS 6.5发行版,内核版本号3.11 1. Linux抓包源程序 在OSI七层模型中.网卡工作在物理层和数据链路层的MAC子层. 进行网络通信时.源主机通过socket( ...
- PostgreSQL9.3:JSON 功能增强 根据PQ中文论坛francs 给出的东西结合自己的摸索总结下
在 PostgreSQL 9.2 版本中已经支持 JSON 类型,不过支持的操作非常有限,仅支持以下函数 array_to_json(anyarray [, pretty_bool]) row_ ...