Java并发编程--CyclicBarrier
概述
CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障(Barrier)。
CyclicBarrier支持一个可选的Runnable命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。
使用
提供的方法:
//parties表示屏障拦截的线程数量,当屏障撤销时,先执行barrierAction,然后在释放所有线程
public CyclicBarrier(int parties, Runnable barrierAction)
//barrierAction默认为null
public CyclicBarrier(int parties) /*
*当前线程等待直到所有线程都调用了该屏障的await()方法
*如果当前线程不是将到达的最后一个线程,将会被阻塞。解除阻塞的情况有以下几种
* 1)最后一个线程调用await()
* 2)当前线程被中断
3)其他正在该CyclicBarrier上等待的线程被中断
4)其他正在该CyclicBarrier上等待的线程超时
5)其他某个线程调用该CyclicBarrier的reset()方法
*如果当前线程在进入此方法时已经设置了该线程的中断状态或者在等待时被中断,将抛出InterruptedException,并且清除当前线程的已中断状态。
*如果在线程处于等待状态时barrier被reset()或者在调用await()时 barrier 被损坏,将抛出 BrokenBarrierException 异常。
*如果任何线程在等待时被中断,则其他所有等待线程都将抛出 BrokenBarrierException 异常,并将 barrier 置于损坏状态。 *如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作(barrierAction),那么在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。
*
*返回值为当前线程的索引,0表示当前线程是最后一个到达的线程
*/
public int await() throws InterruptedException, BrokenBarrierException
//在await()的基础上增加超时机制,如果超出指定的等待时间,则抛出 TimeoutException 异常。如果该时间小于等于零,则此方法根本不会等待。
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException //将屏障重置为其初始状态。如果所有参与者目前都在屏障处等待,则它们将返回,同时抛出一个BrokenBarrierException。
public void reset()
对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。
使用示例:
每个Worker处理矩阵中的一行,在处理完所有的行之前,该线程将一直在屏障处等待。在各个WOrker处理完所有行后,将执行提供的Runnable屏障操作。
class Solver {
final int N; //矩阵的行数
final float[][] data; //要处理的矩阵
final CyclicBarrier barrier; //循环屏障 class Worker implements Runnable {
int myRow;
Worker(int row) { myRow = row; }
public void run() {
while (!done()) {
processRow(myRow); //处理指定一行数据 try {
barrier.await(); //在屏障处等待直到
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
} public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
//初始化CyclicBarrier
barrier = new CyclicBarrier(N, new Runnable() {
public void run() {
mergeRows(...); //合并行
}
});
for (int i = 0; i < N; ++i)
new Thread(new Worker(i)).start(); waitUntilDone();
}
}
实现原理
基于ReentrantLock和Condition机制实现。除了getParties()方法,CyclicBarrier的其他方法都需要获取锁。
域
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock(); //可重入锁
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties; //拦截的线程数量
/* The command to run when tripped */
private final Runnable barrierCommand; //当屏障撤销时,需要执行的屏障操作
//当前的Generation。每当屏障失效或者开闸之后都会自动替换掉。从而实现重置的功能。
private Generation generation = new Generation(); /**
* Number of parties still waiting. Counts down from parties to 0
* on each generation. It is reset to parties on each new
* generation or when broken.
*/
private int count; //还能阻塞的线程数(即parties-当前阻塞的线程数),当新建generation或generation被破坏时,count会被重置。因为对Count的操作都是在获取锁之后,所以不需要其他同步措施。 //静态内联类
private static class Generation {
boolean broken = false; //当前的屏障是否破坏
}
await()
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen;
}
} private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock(); //获取锁
try {
//保存此时的generation
final Generation g = generation;
//判断屏障是否被破坏
if (g.broken)
throw new BrokenBarrierException();
//判断线程是否被中断,如果被中断,调用breakBarrier()进行屏障破坏处理,并抛出InterruptedException
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
} int index = --count; //剩余count递减,并赋值给线程索引,作为方法的返回值
//如果线程索引将为0,说明当前线程是最后一个到达的线程。执行可能存在的屏障操作 barrierCommand,设置下一个Generation。相当于每次开闸之后都进行了一次reset。
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run(); //同步执行barrierCommand
ranAction = true;
nextGeneration(); //执行成功设置下一个nextGeneration
return 0;
} finally {
if (!ranAction) //如果barrierCommand执行失败,进行屏障破坏处理
breakBarrier();
}
} //如果当前线程不是最后一个到达的线程
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await(); //调用Condition的await()方法阻塞
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos); //调用Condition的awaitNanos()方法阻塞
} catch (InterruptedException ie) {
//如果当前线程被中断,则判断是否有其他线程已经使屏障破坏。若没有则进行屏障破坏处理,并抛出异常;否则再次中断当前线程
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
//这种捕获了InterruptException之后调用Thread.currentThread().interrupt()是一种通用的方式。其实就是为了保存中断状态,从而让其他更高层次的代码注意到这个中断。
}
}
//如果屏障被破坏,当前线程抛BrokenBarrierException
if (g.broken)
throw new BrokenBarrierException(); //如果已经换代,直接返回index(last thread已经执行的nextGeneration,但当前线程还没有执行到该语句)
if (g != generation)
return index; //超时,进行屏障破坏处理,并抛TimeoutException
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock(); //释放锁
}
} //将当前屏障置为破坏状态、重置count、并唤醒所有被阻塞的线程。
//必须先获取锁,才能调用此方法
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
} //唤醒trip上等待的所有线程,设置下一个Generation
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
reset()
//重置屏障,先进行屏障破坏处理,再设置下一代generation
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
CyclicBarrier与CountDownLatch比较
1)CountDownLatch:一个线程(或者多个),等待另外N个线程完成某个事情之后才能执行;CyclicBarrier:N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
2)CountDownLatch:一次性的;CyclicBarrier:可以重复使用。
3)CountDownLatch基于AQS;CyclicBarrier基于锁和Condition。本质上都是依赖于volatile和CAS实现的。
参考资料
JDK Doc
《Java并发编程的艺术》
Java并发编程--CyclicBarrier的更多相关文章
- 【Java并发编程实战】-----“J.U.C”:CyclicBarrier
在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...
- Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semaphore、Phaser)
我在<JDK1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...
- Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semphore、Phaser)
我在<jdk1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore (总结)
下面对上面说的三个辅助类进行一个总结: 1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待 ...
- 14、Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...
- 【转】Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java并发编程:CountDownLatch.CyclicBarrier和Semaphore Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在j ...
- java并发编程JUC第十篇:CyclicBarrier线程同步
在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...
- Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo
Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...
随机推荐
- c# .net 3.5 4.0 4.5 5.0 6.0各个版本新特性战略规划总结【转载】
引用:http://blog.csdn.net/attilax/article/details/42014327 c# .net 3.5 4.0 各个版本新特性战略规划总结 1. ---------- ...
- php - empty() is_null() isset()的区别
empty():当变量存在,并且是一个非空非零的值时,返回 FALSE,否则返回 TRUE. is_null():如果指定变量为 NULL,则返回 TRUE,否则返回 FALSE. isset():如 ...
- POJ:1222-EXTENDED LIGHTS OUT(矩阵反转)
EXTENDED LIGHTS OUT Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 12956 Accepted: 8186 ...
- poj 3111 卖珠宝问题 最大化平均值
题意:有N件分别价值v重量w的珠宝,希望保留k件使得 s=v的和/w的和最大 思路:找到贡献最大的 设当前的s为mid(x) 那么贡献就是 v-w*x 排序 ,取前k个 bool operator&l ...
- debug注意事项
1 先看关键代码是否正确,然后查一遍是否有变量名打错. 2 再看初始化有没有问题 3 再把范围开大和开int64(这应该刚开始看题就要注意,在不爆内存的情况下开int64) 4 静态调试,输出中间值. ...
- Android面试收集录3 ContentProvider详解
1.ContentProvider简单介绍 1.1.定义 ContentProvider,即内容提供者属于Android的四大组件之一. 1.2.作用 进程间进行数据交互&共享,即跨进程通信. ...
- 1- js vue.js
1 js 2 Vue.js
- springboot 入门2 开发环境与生产环境采用不同配置问题
目开发中我们通常有两套配置信息 分别配置了我们的数据源信息等? 那么我们要如何不通过修改配置文件大量配置来实现简单的修改与配置来实现相关配置加载功能 首先springboot 有一个核心的配置文件a ...
- Python 实现随机打乱字符串
from random import shuffle def shuffle_str(s): # 将字符串转换成列表 str_list = list(s) # 调用random模块的shuffle函数 ...
- 获取访问者ip及其所在城市
原本使用新浪Api,然后发现不行了,以下小编重新查找了几个,个人推荐太平洋的接口 1.首先获取真实ip $ip = $_SERVER['REMOTE_ADDR']; 2.要知道的Api接口 几个接口$ ...