1. CyclicBarrier的介绍与源码分析

CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。线程进入屏障通过CyclicBarrier的await()方法。

CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction这个Runnable对象,方便处理更复杂的业务场景。

构造函数

public CyclicBarrier(int parties) {
this(parties, null);
}
public int getParties() {
return parties;
}

实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用CyclicBarrier的await方法时,将剩余拦截的线程数减1,然后判断剩余拦截数是否为0,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁,接着先从await方法返回,再从CyclicBarrier的await方法中返回。

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 {
final Generation g = generation; if (g.broken)
throw new BrokenBarrierException(); if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
} int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
} // loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} 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方法时,会在return 0 返回之前调用finally块中的breakBarrier方法。

breakBarrier源代码

private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}

CyclicBarrier主要用于一组线程之间的相互等待,而CountDownLatch一般用于一组线程等待另一组些线程。实际上可以通过CountDownLatch的countDown()和await()来实现CyclicBarrier的功能。即 CountDownLatch中的countDown()+await() = CyclicBarrier中的await()。注意:在一个线程中先调用countDown(),然后调用await()。

其它方法:CycliBarrier对象可以重复使用,重用之前应当调用CyclicBarrier对象的reset方法。

reset源码

public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}

2. 使用示例

package javalearning;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CyclicBarrierDemo {
private CyclicBarrier cb = new CyclicBarrier(4);
private Random rnd = new Random(); class TaskDemo implements Runnable{
private String id;
TaskDemo(String id){
this.id = id;
}
@Override
public void run(){
try {
Thread.sleep(rnd.nextInt(1000));
System.out.println("Thread " + id + " will wait");
cb.await();
System.out.println("-------Thread " + id + " is over");
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
} public static void main(String[] args){
CyclicBarrierDemo cbd = new CyclicBarrierDemo();
ExecutorService es = Executors.newCachedThreadPool();
es.submit(cbd.new TaskDemo("a"));
es.submit(cbd.new TaskDemo("b"));
es.submit(cbd.new TaskDemo("c"));
es.submit(cbd.new TaskDemo("d"));
es.shutdown();
}
}

在这个示例中,我们创建了四个线程a、b、c、d,这四个线程提交给了线程池。四个线程不同时间到达cb.await()语句,当四个线程都输出“Thread x will wait”以后才会输出“Thread x is over”。

运行结果

Thread d will wait

Thread a will wait

Thread c will wait

Thread b will wait

-------Thread b is over

-------Thread d is over

-------Thread a is over

-------Thread c is over

3. 参考内容

[1] http://ifeve.com/concurrency-cyclicbarrier/

Java并发包中CyclicBarrier的工作原理、使用示例的更多相关文章

  1. Java并发包中Semaphore的工作原理、源码分析及使用示例

    1. 信号量Semaphore的介绍 我们以一个停车场运作为例来说明信号量的作用.假设停车场只有三个车位,一开始三个车位都是空的.这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦.以后 ...

  2. Java并发包中CountDownLatch的工作原理、使用示例

    1. CountDownLatch的介绍 CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch 的作用和 Thread.join() 方法类似,让一些线 ...

  3. Java并发包中Lock的实现原理

    1. Lock 的简介及使用 Lock是java 1.5中引入的线程同步工具,它主要用于多线程下共享资源的控制.本质上Lock仅仅是一个接口(位于源码包中的java\util\concurrent\l ...

  4. Java并发包中CyclicBarrier的源码分析和使用

    CyclicBarrier的介绍和源码分析 CyclicBarrier的字母意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞 ...

  5. Java并发包中线程池ThreadPoolExecutor原理探究

    一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...

  6. Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理

    Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...

  7. CyclicBarrier的工作原理及其实例

    CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题. 1.CyclicBarrier的工作原理 CyclicBarrier大致是可循环利用的屏障,顾名思义,这 ...

  8. Java 并发包中的读写锁及其实现分析

    1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时 刻可以允许多个读线程访问,但是在写线程访问时,所有 ...

  9. 转:ListView中getView的工作原理

    ListView中getView的工作原理: [1]ListView asks adapter “give me a view” (getView) for each item of the list ...

随机推荐

  1. ODBC,OLEDB,ADO,ADO.net,JDBC 理解

    一 .ODBC 开放式数据库互联(Open Database connectivity), 93年微软推出的实现应用程序 和关系数据库直接 通讯的借口标准.只能用于关系数据库 . 注意事项 : 必须先 ...

  2. 大文件下载控件(down2)-示例更新-Xproer.HttpDownloader

    版权所有 2009-2016 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/down2/i ...

  3. Django-数据模型

    修改model.py文件 from django.db import models # Create your models here. class Question(models.Model): q ...

  4. Log4j 与 Logback的ConversionPattern对比

    为了能将log4j的配置无缝转到logback,需要了解其中ConversionPattern的差异,以下是对比表格,内容来自: log4j官网 logback官网 其中可能需要转换的地方主要有两块: ...

  5. 修改Chrome临时文件位置

    通过目录链接实现. mklink /D "C:\Users\ljq\AppData\Local\Google\Chrome\User Data" z:\temp http://we ...

  6. php中explode与split的区别介绍

    php中explode与split的区别介绍 作者: 字体:[增加 减小] 类型:转载 今天在使用split时遇到一些问题.还是对函数理解不深刻,特写出来做个记 首先来看下两个方法的定义: 函数原型: ...

  7. mfc

    (1)编写mfc程序的基本步骤: 1.资源编辑: 2.关联控件变量: 3.编写新的类和成员函数: 4.添加消息处理函数. (2)消息映射机制: (3)常用控件: (4)界面美化:

  8. python之raise、assert、with/as环境管理器

    要故意出发异常,可以使用raise语句,形式如下: raise <name>  #manually trigger an exception raise<name>,<v ...

  9. slot游戏中的数学概念

    最近研究slot 算法,看了大量的英文资料,因为母语中文,一直使用中文的英文小白来说,好心塞,悔不当初没学好英文. 下文是从众多的英文中摘录的唯一能够看明白的概念.先给自己留着,到时候深入研究可以看 ...

  10. poj 1286&&poj2409 Polya计数 颜色匹配

    #include <iostream> #include <math.h> using namespace std; #define LL long long LL gcd(L ...