一、CyclicBarrier使用

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

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

public static voidmain(String[] args) {

CyclicBarrier cb= new CyclicBarrier(3, newRunnable() {public voidrun() {

System.out.println("所有线程集合");

}

});for (int i = 0; i < 3; i++) {new CyclicBarrierThread(i + "", cb).start();

}

}

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

public class CyclicBarrierThread extendsThread{privateCyclicBarrier barrier;privateString name;private intcount;publicCyclicBarrierThread(String name,CyclicBarrier barrier) {this.name=name;this.barrier=barrier;this.count=0;

}public voidrun() {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开始计数:10号线程---Thread-0开始计数:11号线程---Thread-1开始计数:12号线程---Thread-2开始计数:21号线程---Thread-1开始计数:20号线程---Thread-0开始计数:2所有线程集合

2号线程---Thread-2集合完毕,继续计数

1号线程---Thread-1集合完毕,继续计数

0号线程---Thread-0集合完毕,继续计数

2号线程---Thread-2开始计数:31号线程---Thread-1开始计数:30号线程---Thread-0开始计数:32号线程---Thread-2开始计数:40号线程---Thread-0开始计数:41号线程---Thread-1开始计数:4所有线程集合

1号线程---Thread-1集合完毕,继续计数

2号线程---Thread-2集合完毕,继续计数

0号线程---Thread-0集合完毕,继续计数

0号线程---Thread-0开始计数:52号线程---Thread-2开始计数:51号线程---Thread-1开始计数:50号线程---Thread-0开始计数:61号线程---Thread-1开始计数:62号线程---Thread-2开始计数:6所有线程集合

2号线程---Thread-2集合完毕,继续计数

0号线程---Thread-0集合完毕,继续计数

1号线程---Thread-1集合完毕,继续计数

0号线程---Thread-0开始计数:71号线程---Thread-1开始计数:72号线程---Thread-2开始计数:71号线程---Thread-1开始计数:80号线程---Thread-0开始计数:82号线程---Thread-2开始计数:8所有线程集合

2号线程---Thread-2集合完毕,继续计数

0号线程---Thread-0集合完毕,继续计数

1号线程---Thread-1集合完毕,继续计数

0号线程---Thread-0开始计数:91号线程---Thread-1开始计数:92号线程---Thread-2开始计数:91号线程---Thread-1开始计数:100号线程---Thread-0开始计数:102号线程---Thread-2开始计数:10所有线程集合

1号线程---Thread-1集合完毕,继续计数

2号线程---Thread-2集合完毕,继续计数

0号线程---Thread-0集合完毕,继续计数

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

二、CyclicBarrier源码分析

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

1、CyclicBarrier的构造

首先看下CyclicBarrier的构造函数

public CyclicBarrier(intparties, Runnable barrierAction) {if (parties <= 0) throw newIllegalArgumentException();//拦截的线程数量

this.parties =parties;//用于计数的count值,每有一个线程执行到屏障点,就会递减1

this.count =parties;//定义的拦截线程

this.barrierCommand =barrierAction;

}

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

2、await方法

public int await() throwsInterruptedException, BrokenBarrierException {try{return dowait(false, 0L);

}catch(TimeoutException toe) {throw new Error(toe); //cannot happen

}

}

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

private int dowait(boolean timed, longnanos)throwsInterruptedException, BrokenBarrierException,

TimeoutException {//获取可重入锁

final ReentrantLock lock = this.lock;//加锁

lock.lock();try{//CyclicBarrier内部定义的一个Generation类

final Generation g =generation;//判断Generation的broken状态

if(g.broken)throw newBrokenBarrierException();//如果线程被中断

if(Thread.interrupted()) {//Generation的broken置为true,count值重置,并唤醒所有线程

breakBarrier();throw newInterruptedException();

}//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();throwie;

}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 newBrokenBarrierException();if (g !=generation)returnindex;if (timed && nanos <= 0L) {

breakBarrier();throw newTimeoutException();

}

}

}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 voidreset() {final ReentrantLock lock = this.lock;

lock.lock();try{//generation.broken设置为true,唤醒所有线程,count值重置

breakBarrier();

nextGeneration();

}finally{

lock.unlock();

}

}private voidnextGeneration() {//signal completion of last generation

trip.signalAll();//set up next generation

count =parties;

generation= newGeneration();

}private voidbreakBarrier() {

generation.broken= true;

count=parties;

trip.signalAll();

}

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

4、getNumberWaiting方法

public intgetNumberWaiting() {final ReentrantLock lock = this.lock;

lock.lock();try{return parties -count;

}finally{

lock.unlock();

}

}

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

三、总结

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

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

java线程同步barrier_Java多线程同步工具类之CyclicBarrier相关推荐

  1. 多线程-并发工具类之CyclicBarrier详解

    文章目录 简介 例子 实现原理 小结 简介 从字面意思理解,CyclicBarrier是回环屏障的意思,它可以让一组线程全部达到一个状态后再全部同时执行.这里之所以叫作回环是因为当所有等待线程执行完毕 ...

  2. Java之多线程下载工具类

    1.多线程下载工具类 import java.net.URL; import java.io.InputStream; import java.io.RandomAccessFile; import ...

  3. (10)Java泛型-Map集合-集合框架工具类-可变参数-静态导入

    -- 部分1.5新特性Java泛型-Map集合-集合框架工具类 泛型 概述: JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制. 对于泛型可以这样理解: 没有使用泛型时,只要是对 ...

  4. java线程设计模式_JAVA多线程设计模式

    漫谈UML UML 类图 类和层次结构的关系 接口与实现 聚合 访问控制 类间的关联性 顺序图 处理流程和对象间的协调 时序图 Introduction 1 Java语言的线程 Java语言的线程 何 ...

  5. Java学习总结:58(Collections工具类)

    Collections工具类 Java提供了一个集合的工具类--Collections,这个工具类可以实现List.Set.Map集合的操作.Collections类的常用方法如下: No. 方法 类 ...

  6. java配置文件工具类,java项目加载配置文件的工具类

    java项目加载配置文件的工具类 package com.loadproperties; import java.io.IOException; import java.io.InputStream; ...

  7. Java实现Google的S2算法工具类

    WGS84坐标系 GCJ02坐标系 BD09坐标系的各种转换 WGS84坐标系 GCJ02坐标系 BD09坐标系的各种转换 Google S2 经纬度 转 CellId 经纬度 转 cellToken ...

  8. Java 图片添加数字暗水印工具类

    Java 图片添加数字暗水印工具类. package cnki.thesis.common.utils;import org.opencv.core.*;import java.util.ArrayL ...

  9. Java时间戳与日期格式转换工具类

    Java时间戳与日期格式转换工具类 在做web开发时,有时候需要用到时间戳,在前台进行日期转换,从数据库中取出来是日期格式的,这里记录下使用的转换工具类(这些都是静态方法,通过类名.方法的形式即可调用 ...

最新文章

  1. mysql主从及读写分离
  2. yum安装zlib出错
  3. ACM Smallest Difference
  4. css搜索的文本框,一个很不错的CSS改写的大表单文本框和搜索按钮组
  5. [Android 插件化(一)] DynamicLoadApk的用法
  6. spark ui_Spark UI的见解
  7. 动态规划——最长公共子序列长度
  8. python代码实例sicket_Python socket聊天脚本代码实例
  9. [Usaco2007 Dec]宝石手镯[01背包][水]
  10. wmic cpu get processorid获取的都一样_DJL 之 Java 玩转多维数组,就像 NumPy 一样
  11. 今日头条php面试经验,「今日头条」前端面试题和思路解析
  12. ES2018 学习笔记(4)Unicode 和 ISO 10646
  13. web安全day17:天天都在说的中间人攻击到底是啥
  14. Linux 安装 菜鸟教程,Linux安装Nginx(菜鸟教程简单易懂)
  15. 动态规划背包问题之完全背包详解
  16. OMNeT 例程 Tictoc16 学习笔记
  17. scrapy下载图片(《精通scrapy网络爬虫》第九章)
  18. Python练手项目:计算机自动还原魔方(4)还原底部两层+顶面
  19. JavaWeb 获取客户端的真实IP地址
  20. 学1个月爬虫就月赚6000?别被骗了,老师傅告诉你爬虫的真实情况

热门文章

  1. mysql 5.5.35 单机多实例配置详解_MySQL 5.5.35 单机多实例配置详解
  2. php smarty安装,php smarty 安装 、配置、使用 及缓存cache的配置使用
  3. 【Paper】2019_带有不匹配干扰的多智能体系统有限时间积分滑模控制
  4. P10 线性系统状态空间设计-《Matlab/Simulink与控制系统仿真》程序指令总结
  5. STM32 基础系列教程 30 - 文件系统
  6. 【任务脚本】0528京东618叠蛋糕任务脚本全自动脚本,京东任务全自动程序稳定运行,向大神致敬...
  7. 【arduino】在Arduino上运行FreeRTOS操作系统,freeRTOS入门教程helloword
  8. 基于随机接入代价的异构网络速率分配算法研究
  9. 「从源码中学习」面试官都不知道的Vue题目答案
  10. [Eclipse] - 解决导入flask模块出现的Unresolved Import flask问题