场景描述:

  多线程设计过程中,经常会遇到需要等待其它线程结束以后再做其他事情的情况。
有几种方案:
 
  1.在主线程中设置一自定义全局计数标志,在工作线程完成时,计数减1。主线程侦测该标志是否为0,一旦为0,表示所有工作线程已经完成。
  2.使用Java标准的类CountDownLatch来完成这项工作,原理是一样的,计数。
 
 

CountDownLatch

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 
其机制是:
  当多个(具体数量等于初始化CountDownLatch时的count参数的值)线程都达到了预期状态或完成预期工作时触发事件,其他线程可以等待这个事件来触发自己的后续工作。这里需要注意的是,等待的线程可以是多个,即CountDownLatch是可以唤醒多个等待的线程的。达到自己预期状态的线程会调用CountDownLatch的countDown方法,而等待的线程会调用CountDownLatch的await方法。
CountDownLatch 很适合用来将一个任务分为n个独立的部分,等这些部分都完成后继续接下来的任务,CountDownLatch 只能出发一次,计数值不能被重置。

流程图

如上图所示,当7个线程都完成latch.countDown调用后,最下面那条线程会从latch.await返回,继续执行后面的代码

函数列表

  • CountDownLatch(int count) :构造一个用给定计数初始化的 CountDownLatch。
  • void await():使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
  • boolean await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
  • void countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

实现原理

 

实例

  我们来看一个具体的例子。假设我们使用一台多核的机器对一组数据进行排序,那么我们可以把这组数据分到不同线程中进行排序,然后合并;可以利用线程池来管理多线程;可以将CountDownLatch用作各个分组数据都排好序的通知。下面是代码片段:

先看主线程

int count = ;
final CountDownLatch latch = new CountDownLatch(count);
int[] datas = new int[];
int step = datas.length / count;
for (int i=; i < count; i++) {
int start = i * step;
int end = (i+) * step;
if (i == count - ) end = datas.length;
threadpool.execute(new MyRunnable(latch, datas, start, end));
}
latch.await();
//合并数据

我们再看一下具体任务的代码,即MyRunnable的run方法的实现:

public void run() {
//数据排序
latch.countDown();
}

CyclicBarrier

可以协同多个线程,让多个线程在这个屏障前等待,直到所有线程都达到了这个屏障时,再一起继续执行后面的动作。
CyclicBarrier适用于多个线程有固定的多步需要执行,线程间互相等待,当都执行完了,再一起执行下一步。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
 

流程图

 上图中的7个线程各有一个barrier.await,那么任何一个线程在执行到barrier.await时就会进入阻塞等待状态,直到7个线程都到了barrier.await时才会同时从await返回,继续后面的工作。此外如果在构造CyclicBarrier时设置了一个Runnable实现,那么最后一个到barrier.await的线程会执行这个Runnable的run方法,以完成一些预设的工作。
 
注意比较CountDownLatchCyclicBarrier
  (01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
  (02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
CountDownLatch 适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景;CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,例如同时开始一个工作。另外,CyclicBarrier 的循环特性和构造函数所接受的 Runnable 参数也是 CountDownLatch 所不具备的。

 
 
 
CountDownLatch
CyclicBarrier
适用场景
主线程等待其他工作线程结束
多个线程相互等待,直到所有线程都达到一个障碍点Barrier
主要方法
CountDownLatch(int count) 主线程调用:初始化计数
 
await() 主线程调用 : 阻塞,直到等待计数为0时解除阻塞 
 
countDown() 工作线程调用 : 计数减1
CyclicBarrier(int parties , Runnnable barrierAction) : 初始化参与者数量和障碍点执行Action,action可选,由主线程初始化
 
await() : 由工作线程调用,每被调用一次,计数便会减少1,并阻塞住当前线程 , 直到所有线程都达到障碍点
等待结束
各线程之间不再相互影响, 可以继续做自己的事情, 不再执行下一个工作目标。
在障碍点到达后, 允许所有线程继续执行,到达下一个目标后,可以恢复使用CyclicBarrier, barrier 在释放等待线程后可以重用
异常
 
如果其中一个线程由于中断、错误、或者超时导致永久离开障碍点,其他线程也将抛出异常。

 

实例

   

int count = ;
final CyclicBarrier barrier = new CyclicBarrier(count + );
int[] datas = new int[];
int step = datas.length / count;
for (int i=; i < count; i++) {
int start = i * step;
int end = (i+) * step;
if (i == count - ) end = datas.length;
threadpool.execute(new MyRunnable(barrier, datas, start, end));
}
barrier.await();
//合并数据

可以看到CyclicBarrier对象传入的参数值比CountDownLatch大1,原因是构造CountDownLatch的参数是调用countDown的数量,而CyclicBarrier的数量是await的数量

public void run() {
//数据排序
try {
barrier.await();
}catch (...)
}

Semaphore

Semaphore 信号量对象管理的信号就像令牌,构造时传入个数,总数就是控制并发的数量。我们需要控制并发的代码,执行前先获取信号(通过acquire获取信号许可),执行后归还信号(通过release归还信号许可)。每次acquire成功返回后,Semaphore可用的信号量就会减少一个,如果没有可用的信号,acquire调用就会阻塞,等待有release调用释放信号后,acquire才会得到信号并返回。
如果Semaphore管理的信号量为1个,那么就退化到互斥锁了;如果多于一个信号量,则主要用于控制并发数。与通过控制线程数来控制并发数的方式相比,通过Semaphore来控制并发数可以控制得更加细粒度,因为真正被控制最大并发的代码放到acquire和release之间就行了。
  Semaphore类位于java.util.concurrent包下,它提供了2个构造器:

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

实例

   例如我们需要控制远程方法的并发量,超过并发量的方法就等待有其他方法执行返回后再执行,那么其代码如下:
semaphore.acquire();
try {
//调用远程通信的方法
}
finally {
semaphore.release();
}
 
 
 

参考资料:

Java多线程:CountDownLatch、CyclicBarrier 和 Semaphore的更多相关文章

  1. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  2. CountDownLatch CyclicBarrier和 Semaphore

    CountDownLatch CyclicBarrier和 Semaphore 原理 基于AQS实现. 让需要的暂时阻塞的线程,进入一个死循环里面,得到某个条件后再退出循环,以此实现阻塞当前线程的效果 ...

  3. java 多线程 day15 CyclicBarrier 路障

    import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.ut ...

  4. Java多线程-CountDownLatch、CyclicBarrier、Semaphore

    上次简单了解了多线程中锁的类型,今天要简单了解下多线程并发控制的一些工具类了. 1. 概念说明: CountDownLatch:相当于一个待执行线程计数器,当计数减为零时表示所有待执行线程都已执行完毕 ...

  5. CountDownLatch, CyclicBarrier and Semaphore

    Reference: [1] http://shazsterblog.blogspot.co.uk/2011/12/comparison-of-countdownlatch.html CountDow ...

  6. Java多线程系列——信号量:Semaphore

    简介 信号量为多线程协作提供了更为强大的控制方法.也可以说,信号量是对锁的扩展.无论是内部锁 synchronized 还是重入锁 ReentrantLock,一次都只允许一个线程访问一个资源,而信号 ...

  7. java多线程 -- CountDownLatch 闭锁

    CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CountDownLatch.由于调用了 countDown ...

  8. Java并发--CountDownLatch CyclicBarrier ReentrantLock

    CountDownLatch CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行.CountDownLatch使用一个数字count初始化,使 ...

  9. Java多线程-新特征-信号量Semaphore

    简介信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确.合理的使用公共资源. 概念Semaphore分为单值和多值两种,前者只能 ...

  10. java多线程-CountDownLatch

    简介 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待.用给定的计数 初始化 CountDownLatch.由于调用了 countDown() 方法,所以在当前计数 ...

随机推荐

  1. GBT 33200-2016 社会治安综合治理 综治中心建设与管理规范 GBT 31000-2015 社会治安综合治理基础数据规范

    阚总发的两个国标的标准文件, 看看里面对于数据和问题的分类等. 我们出统计分析,可以按照标准出各个大类小类的各种指标数据. 结合这几天给潍坊弄的12345的报告, 整理出一个可以结合吴中现有平台数据, ...

  2. 2017-9-14-Linux移植:加快Linux主机的启动速度

    参考文章:http://www.mintos.org/skill/fast-boot.html 今天本来不打算写Blog了,Linux笔记本开机太慢了,浪费生命.何不干脆写一篇关于加快Linux主机启 ...

  3. 数学——Euler方法求解微分方程详解(python3)

    算法的数学描述图解 实例 用Euler算法求解初值问题 \[ \frac{dy}{dx}=y+\frac{2x}{y^2}\] 初始条件\(y(0)=1\),自变量的取值范围\(x \in [0, 2 ...

  4. 机器学习系列-tensorflow-03-线性回归Linear Regression

    利用tensorflow实现数据的线性回归 导入相关库 import tensorflow as tf import numpy import matplotlib.pyplot as plt rng ...

  5. LeetCode(509. 斐波那数)

    问题描述: 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...

  6. 潭州课堂25班:Ph201805201 tornado 项目 第四课 增加用户注册登录(课堂笔记)

    tornado 相关说明 在 handlers 中创建个 auth.py 用来做用户登录,在这文件中创建个类,并逐步完善 在 tornado 中创建 login.html 文件,是个登录页面 {% e ...

  7. BZOJ2670 : Almost

    求出前缀和$s[]$,那么区间$[l,r]$的几乎平均数$=\frac{s[r]-s[l-1]}{r-l}$. 若只有一个询问,那么可以维护$(i,s[i-1])$的凸壳,在凸壳上二分点$(i,s[i ...

  8. 深入理解JVM(6)——JVM性能调优实战

    如何在高性能服务器上进行JVM调优:以便充分利用高性能服务器的硬件资源,有两种JVM调优方案. 一.        采用64位操作系统,并为JVM分配大内存 分析:如果JVM中堆内存太小,那么就会频繁 ...

  9. elment ui 图片上传遇到的一些问题

    图片上传返回200,message显示请上传图片 注意上图中的name字段要和服务器接受的name相同,这里我们是imgfile,默认name不是这个,所以要在el-upload组件上设置name属性 ...

  10. JDBC(8)—Blob

    Blob LOB,即:Large Objects(大对象),是用来存储大量的二进制和文本数据的一种数据类型(一个lob字段可以存储多达四个G的数据).LOB分为两种类型:内部LOB和外部LOB --内 ...