CyclicBarrier原理
转载:http://www.cnblogs.com/skywang12345/p/3533995.html
CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
注意比较CountDownLatch和CyclicBarrier:
(01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
(02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
CyclicBarrier函数列表

CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。 CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。 int await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 int await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 int getNumberWaiting() 返回当前在屏障处等待的参与者数目。 int getParties() 返回要求启动此 barrier 的参与者数目。 boolean isBroken() 查询此屏障是否处于损坏状态。 void reset() 将屏障重置为其初始状态。

CyclicBarrier数据结构
CyclicBarrier的UML类图如下:

CyclicBarrier是包含了"ReentrantLock对象lock"和"Condition对象trip",它是通过独占锁实现的。下面通过源码去分析到底是如何实现的。
CyclicBarrier源码分析(基于JDK1.7.0_40)
CyclicBarrier是通过ReentrantLock(独占锁)和Condition来实现的。下面,我们分析CyclicBarrier中3个核心函数: 构造函数, await()作出分析。
1. 构造函数
CyclicBarrier的构造函数共2个:CyclicBarrier 和 CyclicBarrier(int parties, Runnable barrierAction)。第1个构造函数是调用第2个构造函数来实现的,下面第2个构造函数的源码。

public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// parties表示“必须同时到达barrier的线程个数”。
this.parties = parties;
// count表示“处在等待状态的线程个数”。
this.count = parties;
// barrierCommand表示“parties个线程到达barrier时,会执行的动作”。
this.barrierCommand = barrierAction;
}

2. 等待函数
CyclicBarrier.java中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.lock();
try {
// 保存“当前的generation”
final Generation g = generation;
// 若“当前generation已损坏”,则抛出异常。
if (g.broken)
throw new BrokenBarrierException();
// 如果当前线程被中断,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程。
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 将“count计数器”-1
int index = --count;
// 如果index=0,则意味着“有parties个线程到达barrier”。
if (index == 0) { // tripped
boolean ranAction = false;
try {
// 如果barrierCommand不为null,则执行该动作。
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
// 唤醒所有等待线程,并更新generation。
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 当前线程一直阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生,
// 当前线程才继续执行。
for (;;) {
try {
// 如果不是“超时等待”,则调用awati()进行等待;否则,调用awaitNanos()进行等待。
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
// 如果等待过程中,线程被中断,则执行下面的函数。
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
// 如果“当前generation已经损坏”,则抛出异常。
if (g.broken)
throw new BrokenBarrierException();
// 如果“generation已经换代”,则返回index。
if (g != generation)
return index;
// 如果是“超时等待”,并且时间已到,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程。
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
// 释放“独占锁(lock)”
lock.unlock();
}
}

说明:dowait()的作用就是让当前线程阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生,当前线程才继续执行。
(01) generation是CyclicBarrier的一个成员遍历,它的定义如下:
private Generation generation = new Generation();
private static class Generation {
boolean broken = false;
}
在CyclicBarrier中,同一批的线程属于同一代,即同一个Generation;CyclicBarrier中通过generation对象,记录属于哪一代。
当有parties个线程到达barrier,generation就会被更新换代。
(02) 如果当前线程被中断,即Thread.interrupted()为true;则通过breakBarrier()终止CyclicBarrier。breakBarrier()的源码如下:
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
breakBarrier()会设置当前中断标记broken为true,意味着“将该Generation中断”;同时,设置count=parties,即重新初始化count;最后,通过signalAll()唤醒CyclicBarrier上所有的等待线程。
(03) 将“count计数器”-1,即--count;然后判断是不是“有parties个线程到达barrier”,即index是不是为0。
当index=0时,如果barrierCommand不为null,则执行该barrierCommand,barrierCommand就是我们创建CyclicBarrier时,传入的Runnable对象。然后,调用nextGeneration()进行换代工作,nextGeneration()的源码如下:
private void nextGeneration() {
trip.signalAll();
count = parties;
generation = new Generation();
}
首先,它会调用signalAll()唤醒CyclicBarrier上所有的等待线程;接着,重新初始化count;最后,更新generation的值。
(04) 在for(;;)循环中。timed是用来表示当前是不是“超时等待”线程。如果不是,则通过trip.await()进行等待;否则,调用awaitNanos()进行超时等待。
CyclicBarrier的使用示例
示例1
新建5个线程,这5个线程达到一定的条件时,它们才继续往后运行。

1 import java.util.concurrent.CyclicBarrier;
2 import java.util.concurrent.BrokenBarrierException;
3
4 public class CyclicBarrierTest1 {
5
6 private static int SIZE = 5;
7 private static CyclicBarrier cb;
8 public static void main(String[] args) {
9
10 cb = new CyclicBarrier(SIZE);
11
12 // 新建5个任务
13 for(int i=0; i<SIZE; i++)
14 new InnerThread().start();
15 }
16
17 static class InnerThread extends Thread{
18 public void run() {
19 try {
20 System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier.");
21
22 // 将cb的参与者数量加1
23 cb.await();
24
25 // cb的参与者数量等于5时,才继续往后执行
26 System.out.println(Thread.currentThread().getName() + " continued.");
27 } catch (BrokenBarrierException e) {
28 e.printStackTrace();
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 }
33 }
34 }

运行结果:

Thread-1 wait for CyclicBarrier. Thread-2 wait for CyclicBarrier. Thread-3 wait for CyclicBarrier. Thread-4 wait for CyclicBarrier. Thread-0 wait for CyclicBarrier. Thread-0 continued. Thread-4 continued. Thread-2 continued. Thread-3 continued. Thread-1 continued.

结果说明:主线程中新建了5个线程,所有的这些线程都调用cb.await()等待。所有这些线程一直等待,直到cb中所有线程都达到barrier时,这些线程才继续运行!
示例2
新建5个线程,当这5个线程达到一定的条件时,执行某项任务。

1 import java.util.concurrent.CyclicBarrier;
2 import java.util.concurrent.BrokenBarrierException;
3
4 public class CyclicBarrierTest2 {
5
6 private static int SIZE = 5;
7 private static CyclicBarrier cb;
8 public static void main(String[] args) {
9
10 cb = new CyclicBarrier(SIZE, new Runnable () {
11 public void run() {
12 System.out.println("CyclicBarrier's parties is: "+ cb.getParties());
13 }
14 });
15
16 // 新建5个任务
17 for(int i=0; i<SIZE; i++)
18 new InnerThread().start();
19 }
20
21 static class InnerThread extends Thread{
22 public void run() {
23 try {
24 System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier.");
25
26 // 将cb的参与者数量加1
27 cb.await();
28
29 // cb的参与者数量等于5时,才继续往后执行
30 System.out.println(Thread.currentThread().getName() + " continued.");
31 } catch (BrokenBarrierException e) {
32 e.printStackTrace();
33 } catch (InterruptedException e) {
34 e.printStackTrace();
35 }
36 }
37 }
38 }

运行结果:

Thread-1 wait for CyclicBarrier. Thread-2 wait for CyclicBarrier. Thread-3 wait for CyclicBarrier. Thread-4 wait for CyclicBarrier. Thread-0 wait for CyclicBarrier. CyclicBarrier's parties is: 5 Thread-0 continued. Thread-4 continued. Thread-2 continued. Thread-3 continued. Thread-1 continued.

结果说明:主线程中新建了5个线程,所有的这些线程都调用cb.await()等待。所有这些线程一直等待,直到cb中所有线程都达到barrier时,执行新建cb时注册的Runnable任务。
CyclicBarrier原理的更多相关文章
- CyclicBarrier 原理(秒懂)
疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 面试必备 + 面试必备 [博客园总入口 ] 疯狂创客圈 经典图书 : <Sprin ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析
1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...
- ☕【Java深层系列】「并发编程系列」让我们一起探索一下CyclicBarrier的技术原理和源码分析
CyclicBarrier和CountDownLatch CyclicBarrier和CountDownLatch 都位于java.util.concurrent这个包下,其工作原理的核心要点: Cy ...
- 【Java并发编程实战】-----“J.U.C”:CyclicBarrier
在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...
- CountDownLatch,CyclicBarrier,Semaphore
CountDownLatch是倒数,doneSignal = new CountDownLatch(LATCH_SIZE);赋初值后,在主线程中等待doneSignal.await();其它线程中,每 ...
- Java - "JUC" CyclicBarrier源码分析
Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例 CyclicBarrier简介 CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 ...
- 并发包下常见的同步工具类详解(CountDownLatch,CyclicBarrier,Semaphore)
目录 1. 前言 2. 闭锁CountDownLatch 2.1 CountDownLatch功能简介 2.2 使用CountDownLatch 2.3 CountDownLatch原理浅析 3.循环 ...
随机推荐
- Groupon面经Prepare: Sort given a range && Summary: Bucket Sort
首先是如何sort一个只有0和1的数组,要求inplace. follow up是告诉一个range,如何在O(N)时间内sort好 两个pointer可解 package Sorting; impo ...
- 最大密集子图(01分数规划+二分+最小割)POJ3155
题意:给出一副连通图,求出一个子图令g=sigma(E)/sigma(V); h[g]=sigma(E)-g*sigma(V):设G是最优值 则当h[g]>0:g<G h[g]<0, ...
- 树形DP+二分(Information Disturbing HDU3586)
题意:给出一颗数,1结点代表司令部,叶子节点代表前线,边全值代表花费,然后需要在某些边放置一些炸弹,炸弹的能量不能小于该边的费用,且炸掉的总费用不能超过m问炸弹能力最小多少, 分析dfs+二分,二分枚 ...
- 变形--位移 translate()
translate()函数可以将元素向指定的方向移动,类似于position中的relative.或以简单的理解为,使用translate()函数,可以把元素从原来的位置移动,而不影响在X.Y轴上的任 ...
- hdu5322 Hope
设dp[n]为n个数字排列时候的答案,那么可以得到dp方程 dp[n]=Σdp[n-i]*c(n-1,i-1)*(i-1)!*i^2(1<=i<=n) 然后上式可以化成卷积形式,分治FFT ...
- ligerui_ligerTree_001_第一个“树”效果
折叠.展开.有复选框.没有复选框: 源码地址:http://download.csdn.net/detail/poiuy1991719/8571255 效果图: <%@ page languag ...
- Aspose.cell处理Excel
(一)从数据库中读取数据写入Excel中 方法1: 步骤:1.建立一个新的项目,引用动态链接库Aspose.dll 2.见下面的原代码 using System;using System.Collec ...
- 夺命雷公狗---DEDECMS----6快速入门之总结篇
我们dedecms四大表分别是: dede_channeltype(模型表) dede_arctype(栏目表) dede_archives(文章主表) dede_addonXXXX(附加表) 使用d ...
- clock gate cell
clock gate的cell多采用latch的形式,来实现,尽可能避免glitch的产生. 可以的verilog建模方式: module cell_ckgate(TE,E,CP,Q) input ...
- 安装Debian的正确方法
一.说明: Debian7.0.0的安装镜像文件有3个DVD,安装基本系统只用到第一个镜像文件,即DVD1 其它镜像文件是附带的软件包. 附Debian 7.0.0系统镜像下载地址: 32位:http ...