【Java并发编程实战】-----“J.U.C”:CyclicBarrier
在上篇博客(【Java并发编程实战】-----“J.U.C”:Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier。在JDK API中是这么介绍的:
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。
CyclicBarrier分析
CyclicBarrier结构如下:
从上图可以看到CyclicBarrier内部使用ReentrantLock独占锁实现的。其构造函数如下:
CyclicBarrier(int parties):创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
CyclicBarrier(int parties, Runnable barrierAction):创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。
public CyclicBarrier(int parties) {
this(parties, null);
} public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
在CyclicBarrier中,最重要的方法就是await(),在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。其源代码如下:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen;
}
}
await内部调用dowait():
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//独占锁
final ReentrantLock lock = this.lock;
//获取独占锁
lock.lock();
try {
//保存当前"Generation"
final Generation g = generation;
//当前generation“已损坏”,抛出BrokenBarrierException异常
//抛出该异常一般都是某个线程在等待某个处于“断开”状态的CyclicBarrier
if (g.broken)
throw new BrokenBarrierException(); //当前线程中断,通过breakBarrier终止终止CyclicBarrier
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
} //计数器-1
int index = --count;
//如果计数器 == 0
//表示所有线程都已经到位,触发动作(是否执行某项任务)
if (index == 0) { // tripped
boolean ranAction = false;
try {
//barrierCommand线程要执行的任务
final Runnable command = barrierCommand;
//执行的任务!=null,执行任务
if (command != null)
command.run();
ranAction = true;
//唤醒所有等待线程,并更新generation。
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
} //循环一直执行,直到下面三个if一个条件满足才会退出循环
for (;;) {
try {
//如果不是超时等待,则调用await等待
if (!timed)
trip.await();
//调用awaitNanos等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
} //当前generation“已损坏”,抛出BrokenBarrierException异常
//抛出该异常一般都是某个线程在等待某个处于“断开”状态的CyclicBarrier
if (g.broken)
throw new BrokenBarrierException(); //generation已经更新,返回index
if (g != generation)
return index; //“超时等待”,并且时间已到,则通过breakBarrier()终止CyclicBarrier
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放独占锁
lock.unlock();
}
}
在dowait方法中其实处理逻辑还是比较简单的:
1、首先判断该barrier是否已经断开了,如果断开则抛出BrokenBarrierException异常;
2、判断计算器index是否等于0,如果等于0,则表示所有的线程准备就绪,已经到达某个公共屏障点了,barrier可以进行后续工作了(是否执行某项任务(构造函数决定));然后调用nextGeneration方法进行更新换代工作(其中会唤醒所有等待的线程);
3、通过for循环(for(;;))使线程一直处于等待状态。直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生。
在dowait中有Generation这样一个对象。该对象是CyclicBarrier的一个成员变量:
private static class Generation {
boolean broken = false;
}
Generation描述着CyclicBarrier的更显换代。在CyclicBarrier中,同一批线程属于同一代。当有parties个线程到达barrier,generation就会被更新换代。其中broken标识该当前CyclicBarrier是否已经处于中断状态。
对于中断,CyclicBarrier是通过breakBarrier()实现的:
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
在breakBarrier()中除了将broken设置为true,还会调用signalAll将在CyclicBarrier处于等待状态的线程全部唤醒。
在超时的判断中,CyclicBarrier根据timed的值来执行不同的wait。await、awaitNanos都是Condition中的方法。
当index = --count等于0时,标识“有parties个线程到达barrier”,临界条件到达,则执行相应的动作。执行完动作后,则调用nextGeneration进行更新换代:
private void nextGeneration() {
//唤醒所有处于等待状态的线程
trip.signalAll();
//初始化计数器
count = parties;
//产生新的Generation对象
generation = new Generation();
}
示例
1、线程等待到一定条件后才会继续进行。
public class CyclicBarrierTest_1 {
private static CyclicBarrier barrier; static class threadTest1 extends Thread{
public void run() {
System.out.println(Thread.currentThread().getName() + "达到...");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完成...");
}
} public static void main(String[] args) {
barrier = new CyclicBarrier(5);
for(int i = 1 ; i <= 5 ; i++){
new threadTest1().start();
}
}
}
------执行结果:
Thread-0达到...
Thread-1达到...
Thread-3达到...
Thread-2达到...
Thread-4达到...
Thread-4执行完成...
Thread-0执行完成...
Thread-1执行完成...
Thread-2执行完成...
Thread-3执行完成...
2、线程等待到一定条件后,执行某项任务。比如说我们等车,只有当车坐满后,汽车才会发动。
这个只需要对上面的代码进行小动作的改动即可:
public class CyclicBarrierTest_2 {
private static CyclicBarrier barrier; static class threadTest1 extends Thread{
public void run() {
System.out.println(Thread.currentThread().getName() + "达到...");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完成...");
}
} public static void main(String[] args) {
barrier = new CyclicBarrier(5,new Runnable() { @Override
public void run() {
System.out.println("执行CyclicBarrier中的任务.....");
}
});
for(int i = 1 ; i <= 5 ; i++){
new threadTest1().start();
}
}
}
-------执行结果:
Thread-0达到...
Thread-1达到...
Thread-3达到...
Thread-4达到...
Thread-2达到...
执行CyclicBarrier中的任务.....
Thread-2执行完成...
Thread-0执行完成...
Thread-3执行完成...
Thread-1执行完成...
Thread-4执行完成...
参考文献:
1、Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
【Java并发编程实战】-----“J.U.C”:CyclicBarrier的更多相关文章
- 【Java并发编程实战】-----“J.U.C”:CountDownlatch
上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...
- 【Java并发编程实战】-----“J.U.C”:Semaphore
信号量Semaphore是一个控制访问多个共享资源的计数器,它本质上是一个"共享锁". Java并发提供了两种加锁模式:共享锁和独占锁.前面LZ介绍的ReentrantLock就是 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之三unlock方法分析
前篇博客LZ已经分析了ReentrantLock的lock()实现过程,我们了解到lock实现机制有公平锁和非公平锁,两者的主要区别在于公平锁要按照CLH队列等待获取锁,而非公平锁无视CLH队列直接获 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之一简介
注:由于要介绍ReentrantLock的东西太多了,免得各位客官看累,所以分三篇博客来阐述.本篇博客介绍ReentrantLock基本内容,后两篇博客从源码级别分别阐述ReentrantLock的l ...
- 【Java并发编程实战】----- AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...
- 【Java并发编程实战】—– AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...
- Java并发编程实战 02Java如何解决可见性和有序性问题
摘要 在上一篇文章当中,讲到了CPU缓存导致可见性.线程切换导致了原子性.编译优化导致了有序性问题.那么这篇文章就先解决其中的可见性和有序性问题,引出了今天的主角:Java内存模型(面试并发的时候会经 ...
随机推荐
- static,你还敢用吗?(二)
为了压系统,昨天小组在测试环境模拟了一大批订单数据.今天上午查看记录的账单计息日志,发现了一大堆的MySqlException MySql.Data.MySqlClient.MySqlExceptio ...
- Python列表去重
标题有语病,其实是这样的: 假设有两个列表 : L1 = [1,2,3,4] ; L2 = [1,2,5,6] 然后去掉L1中包含的L2的元素 直接这样当然是不行的: def removeExists ...
- 如何快速优化手游性能问题?从UGUI优化说起
WeTest 导读 本文作者从自身多年的Unity项目UI开发及优化的经验出发,从UGUI,CPU,GPU以及unity特有资源等几个维度,介绍了unity手游性能优化的一些方法. 在之前的文 ...
- iOS逆向工程之Hopper+LLDB调试第三方App
LLDB是Low Level Debugger的简称,在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具.使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理 ...
- 《JavaScript设计模式与开发实践》整理
最近在研读一本书<JavaScript设计模式与开发实践>,进阶用的. 一.高阶函数 高阶函数是指至少满足下列条件之一的函数. 1. 函数可以作为参数被传递. 2. 函数可以作为返回值输出 ...
- SQL 数据优化索引建suo避免全表扫描
首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...
- python安装BeautifulSoup注意事项
好久没有写爬虫了,最近用Python的BeautifulSoup4.Scrapy分别对以前写的spider进行优化,发现python3.5后这些库变化了很多,遇到了许多问题,在这里做一下总结. 切换环 ...
- python_单元测试unittest
Python自带一个单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. 步骤1:首先引入unittest模块--import un ...
- [故障公告]受阿里云部分ECS服务器故障影响,目前无法上传图片与文件
抱歉!今天下午阿里云华东1可用区B的部分ECS服务器出现IO HANG问题,受此影响,我们的图片与文件上传服务器无法访问,由此给您带来麻烦,请您谅解. 16:30左右开始,我们发现图片与文件上传服务器 ...
- 工大助手(C#与python交互)
工大助手(爬虫--C#与python交互) 基本内容 工大助手(桌面版) 实现登陆.查成绩.计算加权平均分等功能 团队人员 13070046 孙宇辰 13070003 张帆 13070004 崔巍 1 ...