一、CyclicBarrier使用

CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会触发运行;同时CyclicBarrier相比与CountDownLatch,它是可以被重置的;下面我们通过一个简单例子看下CyclicBarrier的使用;

实例化一个CyclicBarrier对象并传入你要控制的线程内部;

    public static void main(String[] args) {

        CyclicBarrier cb = new CyclicBarrier(3, new Runnable() {
public void run() {
System.out.println("所有线程集合");
}
});
for (int i = 0; i < 3; i++) {
new CyclicBarrierThread(i + "", cb).start();
} }

计数线程代码,每当计数到偶数时调用CyclicBarrier的await()方法

public class CyclicBarrierThread extends Thread{

    private CyclicBarrier barrier;

    private String name;

    private int count;

    public CyclicBarrierThread(String name,CyclicBarrier barrier) {
this.name=name;
this.barrier=barrier;
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()+"开始计数:"+count);
if(count%2==0) {//每计数到偶数次时集合一次
barrier.await();
System.out.println(name+"号线程---"+Thread.currentThread().getName()+"集合完毕,继续计数");
}
} } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}

查看代码输出

2号线程---Thread-2开始计数:1
0号线程---Thread-0开始计数:1
1号线程---Thread-1开始计数:1
2号线程---Thread-2开始计数:2
1号线程---Thread-1开始计数:2
0号线程---Thread-0开始计数:2
所有线程集合
2号线程---Thread-2集合完毕,继续计数
1号线程---Thread-1集合完毕,继续计数
0号线程---Thread-0集合完毕,继续计数
2号线程---Thread-2开始计数:3
1号线程---Thread-1开始计数:3
0号线程---Thread-0开始计数:3
2号线程---Thread-2开始计数:4
0号线程---Thread-0开始计数:4
1号线程---Thread-1开始计数:4
所有线程集合
1号线程---Thread-1集合完毕,继续计数
2号线程---Thread-2集合完毕,继续计数
0号线程---Thread-0集合完毕,继续计数
0号线程---Thread-0开始计数:5
2号线程---Thread-2开始计数:5
1号线程---Thread-1开始计数:5
0号线程---Thread-0开始计数:6
1号线程---Thread-1开始计数:6
2号线程---Thread-2开始计数:6
所有线程集合
2号线程---Thread-2集合完毕,继续计数
0号线程---Thread-0集合完毕,继续计数
1号线程---Thread-1集合完毕,继续计数
0号线程---Thread-0开始计数:7
1号线程---Thread-1开始计数:7
2号线程---Thread-2开始计数:7
1号线程---Thread-1开始计数:8
0号线程---Thread-0开始计数:8
2号线程---Thread-2开始计数:8
所有线程集合
2号线程---Thread-2集合完毕,继续计数
0号线程---Thread-0集合完毕,继续计数
1号线程---Thread-1集合完毕,继续计数
0号线程---Thread-0开始计数:9
1号线程---Thread-1开始计数:9
2号线程---Thread-2开始计数:9
1号线程---Thread-1开始计数:10
0号线程---Thread-0开始计数:10
2号线程---Thread-2开始计数:10
所有线程集合
1号线程---Thread-1集合完毕,继续计数
2号线程---Thread-2集合完毕,继续计数
0号线程---Thread-0集合完毕,继续计数

通过输出结果可以看到,计数线程每计数到偶数次时使用CyclicBarrier的await()方法,线程都会进入阻塞等待的状态,直到最后一个线程到达屏障点时,触发你定义的执行线程,而且CyclicBarrier的await()方法是可以重复使用的。

二、CyclicBarrier源码分析

下面我们就对CyclicBarrier内部的源码实现进行一些分析与总结

1、CyclicBarrier的构造

首先看下CyclicBarrier的构造函数

    public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
//拦截的线程数量
this.parties = parties;
//用于计数的count值,每有一个线程执行到屏障点,就会递减1
this.count = parties;
//定义的拦截线程
this.barrierCommand = barrierAction;
}

CyclicBarrier的构造函数很简单就是接收你要拦截的线程数量与定义的执行线程。

2、await方法

    public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}

我们看下具体实现dowait方法的实现

    private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//获取可重入锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//CyclicBarrier内部定义的一个Generation类
final Generation g = generation; //判断Generation的broken状态
if (g.broken)
throw new BrokenBarrierException(); //如果线程被中断
if (Thread.interrupted()) {
//Generation的broken置为true,count值重置,并唤醒所有线程
breakBarrier();
throw new InterruptedException();
} //count值减一
int index = --count;
if (index == 0) { // 如果conunt为0,说明最后一个线程到大屏障
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();//执行你传入的线程
ranAction = true;
nextGeneration();//唤醒所有阻塞的线程,同时重置count值与Generation
return 0;
} finally {
if (!ranAction)
//拦截线程没有正常执行,唤醒所有线程,同时重置count值,Generation的broken置为true
breakBarrier();
}
} // loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//是否设置阻塞的超时时间
if (!timed)
//释放当前锁
trip.await();//false 表示不设置,一直阻塞
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);//true 设置阻塞的超时时间
} 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();
}
} if (g.broken)
throw new BrokenBarrierException(); if (g != generation)
return index; if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放锁
lock.unlock();
}
}

dowait方法的实现流程是很清晰的,通过ReentrantLock的Condition接口与count值相互配合,主要完成以下功能:

1、当需要拦截的线程到达屏障点调用await方法后获取ReentrantLock锁,保证线程安全;

2、检查count值是否为0,判断是否是最后一个线程到达屏障,如果是的话执行需要触发执行的线程,调用Condition的signalAll方法唤醒所有阻塞的线程,并重置count值与Generation类,保障CyclicBarrier的重复可用;

3、如果不是最后一个线程的话,根据传入的参数调用Condition的await方法释放锁资源并进入阻塞等待,直到被唤醒;

3、reset方法

可以用来主动重置CyclicBarrier的状态

    public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//generation.broken设置为true,唤醒所有线程,count值重置
breakBarrier();
nextGeneration();
} finally {
lock.unlock();
}
} private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
} private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}

breakBarrier()与nextGeneration(),这两个方法的主要区别就在于前者会把generation.broken设置为true,也就是说如果调用reset方法主动重置CyclicBarrier类的状态,当前正在使用CyclicBarrier类同步的线程都会被唤醒或抛出异常;

4、getNumberWaiting方法

    public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}

很明显getNumberWaiting方法使用来获取当前已经运行至屏蔽点并阻塞等待的线程数量的;

三、总结

通过上面分析可以看到CyclicBarrier的实现原理相对还是比较简单与清晰的,主要是基于ReentrantLock与计数器相结合来实现多个线程的同步控制的。以上就是对CyclicBarrier类的使用与内部实现进行的分析,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

Java多线程同步工具类之CyclicBarrier的更多相关文章

  1. Java多线程同步工具类之CountDownLatch

    在过去我们实现多线程同步的代码中,往往使用join().wait().notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实 ...

  2. Java多线程同步工具类之Semaphore

    Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...

  3. Java多线程并发工具类-信号量Semaphore对象讲解

    Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...

  4. Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo

    Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...

  5. java 利用同步工具类控制线程

    前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...

  6. Java多线程并发工具类

    Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问 ...

  7. Java多线程——其他工具类CyclicBarrier、CountDownLatch和Exchange

    CyclicBarrier 适用于:创建一组任务,它们并行地执行任务,然后在进行下一个步骤之前等待,直至所有任务完成.它使得所有的并行任务都将在栅栏处列队,因此可以一致地向前移动. 表示大家彼此等待, ...

  8. 并发包下常见的同步工具类(CountDownLatch,CyclicBarrier,Semaphore)

    在实际开发中,碰上CPU密集且执行时间非常耗时的任务,通常我们会选择将该任务进行分割,以多线程方式同时执行若干个子任务,等这些子任务都执行完后再将所得的结果进行合并.这正是著名的map-reduce思 ...

  9. Java并发——同步工具类

    CountDownLatch  同步倒数计数器 CountDownLatch是一个同步倒数计数器.CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatch对象 ...

随机推荐

  1. 让ProgressDialog在setCancelable(false)时按返回键可dismiss

    最近发现Android4.0系统中ProgressDialog设置为setCancelable(true)时,点击ProgressDialog以外的区域也可以让ProgressDialog dismi ...

  2. poj 3071 可能DP

    http://poj.org/problem? id=3071 推方程不难,可是难在怎么算 dp[i][j]表示第i场时第j仅仅队伍存活下来的概率 方程:dp[i][j]=sigma(dp[i-1][ ...

  3. Mybatis动态建表

    在网上查了很多,都说Mybatis不支持动态建表,心凉了一节.还好找到这么一篇,找到了希望:http://www.zzzyk.com/show/ec5660d9cf1071b3.htm 经过在mysq ...

  4. 帕斯瓦尔定理(Parseval's theorem)

    ∫∞−∞|x(t)|2dt=12π∫∞−∞|X(ω)|2dω=∫∞−∞|X(2πf)|2df∑n=−∞∞|x[n]|2=12π∫π−π|X(eiϕ)|2dϕ∑n=0N−1|x[n]|2=1N∑k=0N ...

  5. 多线程Parallel和Task

    不管是Parallel还是Task,最里面都是线程池(里面是线程)当开启多个任务后,系统会根据当前的线程池的资源进行分配,任务则进行等待Parallel可以对系统的CPU进行设置,可以最大程度上榨干系 ...

  6. WPF RichTextBox 禁止换行

    原文:WPF RichTextBox 禁止换行 这个问题困扰了好久,进过不断的努力,终于解决了           <RichTextBox Margin="0,44,10,0&quo ...

  7. boost库asio详解8——几个TCP的简单例子

    摘于boost官网的几个例子, 做了点小修改, 笔记之. 同步客户端 void test_asio_synclient() { typedef boost::asio::io_service IoSe ...

  8. 模拟QQ窗口抖动效果(通过MoveWindow和Sleep进行模拟)

    RECT rtWindow; GetWindowRect(&rtWindow); //long x = 400; //long y = 200; long x = rtWindow.left; ...

  9. 如何在XAML中转义大括号

    原文:如何在XAML中转义大括号 如何在XAML中转义大括号                                       周银辉 我们知道大括号"{}"在XAML中 ...

  10. 【Gerrit】自动设置reviewer

    gerrit 提供了一种代码review解决方案,但每次代码提交之后都要设置每个commit的code reviewer, 实在是痛苦. gerrit 在官方说明文档里面提供了解决方法,地址:http ...