CyclicBarrier源码分析

  • 一. CyclicBarrier介绍
  • 二. CyclicBarrier源码分析
    • ①. CyclicBarrier的内部类---Generation类
    • ②. CyclicBarrier成员变量
    • ③. CyclicBarrier的构造函数
    • ③. await()方法源码分析
    • ④. dowait()方法
    • ⑤. breakBarrier() 方法
    • ⑥. nextGeneration() 方法
    • ⑦. reset()方法
    • 三. CyclicBarrier特点
    • 四. CyclicBarrier场景实战

一. CyclicBarrier介绍

  1. CyclicBarrier是JDK1.5提供允许一组线程等待彼此都达到一个共同的障碍点的同步的工具。CyclicBarrier适用于固定大小线程池,可以设置一个Runnable任务,当各线程达到共同的障碍点时触发这个任务。
  2. CyclicBarrier就是让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
  3. CyclicBarrier是一种同步机制允许一组线程相互等待,等到所有线程都到达一个屏障点才退出await方法,它没有直接实现AQS而是借助ReentrantLock来实现的同步机制。
  4. 和CountDownLatch相反,需要集齐七颗龙珠,召唤神龙。也就是做加法,开始是0,加到某个值的时候就执行
  5. CyclicBarrier的字面意思就是可循环(cyclic)使用的屏障(Barrier)。它要求做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await方法

二. CyclicBarrier源码分析

①. CyclicBarrier的内部类—Generation类

Generation类有一个属性broken,用来表示当前屏障是否被损坏。

    //静态内部类Generationprivate static class Generation {boolean broken = false;}

②. CyclicBarrier成员变量

该属性有一个为ReentrantLock对象,有一个为Condition对象,而Condition对象又是基于AQS的,所以,归根到底,底层还是由AQS提供支持。

//对扣减屏障值操作进行加锁用。private final ReentrantLock lock = new ReentrantLock();//对扣减屏障值操作后阻塞线程用,private final Condition trip = lock.newCondition();//屏障值最大值,不可修改。private final int parties;//各线程到达屏障执行的任务。private final Runnable barrierCommand;//表示栅栏的当前Generation类型对象,此类型里成员只有一个boolean类型的变量,作用是判断屏障是否被打破。private Generation generation = new Generation();//计数器 ,操作扣减用。private int count;

③. CyclicBarrier的构造函数

//屏障值最大值+Runnable任务public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();//计数器count的初始值被设置为partiesthis.parties = parties;this.count = parties;this.barrierCommand = barrierAction;}

③. await()方法源码分析

    //非定时等待public int await() throws InterruptedException, BrokenBarrierException {try {return dowait(false, 0L);} catch (TimeoutException toe) {throw new Error(toe);}}//定时等待public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException {return dowait(true, unit.toNanos(timeout));}

④. dowait()方法

//核心等待方法private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;//加锁lock.lock();try {final Generation g = generation;//1.如果屏障被打破抛出屏障打破异常if (g.broken)throw new BrokenBarrierException();//2.如果当前线程被中断抛出中断异常if (Thread.interrupted()) {//3.打破屏障breakBarrier();//3.抛出中断异常throw new InterruptedException();}//4.屏障值减一int index = --count;//5.如果减一以后屏障值等于0,就要唤醒所有的阻塞线程if (index == 0) {boolean ranAction = false;try {//唤醒所有线程前先执行指定的任务final Runnable command = barrierCommand;//6.是否配置了任务,如果配置了则执行if (command != null)command.run();ranAction = true;//7.如果任务正常运行结束,所有的阻塞线程,并重置屏障值nextGeneration();return 0;} finally {//8.如果任务运行出现异常,则打破屏障if (!ranAction)breakBarrier();}}//如果计数器不为0则执行此循环for (;;) {try {//9.如果不支持等待超时,则调用await()一直等待if (!timed)trip.await();//10.如果支持等待超时,则等待nanos时间else if (nanos > 0L)nanos = trip.awaitNanos(nanos);} catch (InterruptedException ie) {//11.如果线程被中断。如果g == generation不成立说明当前线程已经被唤醒,这里说明还没被唤醒的中断就要打破屏障,否则就标记中断让上层处理。if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {//若在捕获中断异常前已经完成在栅栏上的等待, 则直接调用中断操作Thread.currentThread().interrupt();}}//11.如果已经打破屏障,抛出BrokenBarrierException异常if (g.broken)throw new BrokenBarrierException();//12.g != generation成立说明已经被激活,这里正常结束if (g != generation)return index;//13.如果超时,则打破屏障抛出超时异常。if (timed && nanos <= 0L) {breakBarrier();throw new TimeoutException();}}} finally {//释放锁lock.unlock();}}
  • dowait(false, 0L)主要逻辑就是将屏障值count减1,然后进入等待,直到count等于0到达屏障点被唤醒。 这里需要注意的是如果一个线程打破屏障,则所有的线程都会被打破抛出BrokenBarrierException异常,并且屏障被打破后如果想继续使用必须调用reset()方法重置。

⑤. breakBarrier() 方法

breakBarrier()就是设置打破屏障状态为ture, 然后唤醒所以其他阻塞线程,其他阻塞唤醒后会抛出BrokenBarrierException异常。

    //打翻当前栅栏private void breakBarrier() {//将当前栅栏状态设置为打翻generation.broken = true;//设置计数器的值为需要拦截的线程数count = parties;//唤醒所有其他线程trip.signalAll();}

⑥. nextGeneration() 方法

nextGeneration()是重新初始化屏障状态的,所以调用这个方法后CyclicBarrier可重用。

    //重新初始化屏障状态的private void nextGeneration() {//唤醒所有其他线程trip.signalAll();//设置计数器的值为需要拦截的线程数count = parties;//初始化屏障状态generation = new Generation();}

⑦. reset()方法

先打破屏障,终止各线程等待状态使其他线程抛出BrokenBarrierException异常,然后重置CyclicBarrier状态,使其可重用。

   //重置一个栅栏public void reset() {final ReentrantLock lock = this.lock;lock.lock();try {//打破屏障breakBarrier();//重置CyclicBarrier状态nextGeneration();} finally {lock.unlock();}}

三. CyclicBarrier特点

  1. 可重用,正常情况下可重复使用,中断情况下在调用reset()后重复使用
  2. 可中断,运行过程中可中断执行
  3. 支持配置Runnable任务,当达到障碍点时可触发

与CountDownLatch比较

  1. 相同点:都是同步屏障,都可以中断

  2. 不同点: CyclicBarrier到达屏障后唤醒全部线程;而CountDownLatch到达屏障后是一个一个传播唤醒。CyclicBarrier支持配置Runnable任务CountDownLatch不支持;CyclicBarrier可重用CountDownLatch不可重用

  3. CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;

  4. CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。

CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。

四. CyclicBarrier场景实战

  • 集齐7个龙珠,召唤神龙的Demo,我们需要首先创建CyclicBarrier

/**
* 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {System.out.println("召唤神龙");
});
  • 然后同时编写七个线程,进行龙珠收集,但一个线程收集到了的时候,我们需要让他执行await方法,等待到7个线程全部执行完毕后,我们就执行原来定义好的方法

package com.xizi.aqs;import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo {public static void main(String[] args) {// 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {System.out.println("召唤神龙");});for (int i = 0; i < 7; i++) {final Integer tempInt = i;new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");try {// 先到的被阻塞,等全部线程完成后,才能执行方法cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}, String.valueOf(i)).start();}}
}

CyclicBarrier---JDK1.8源码分析相关推荐

  1. 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)

    一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...

  2. 【集合框架】JDK1.8源码分析之HashMap(一)

    转载自  [集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大 ...

  3. 【集合框架】JDK1.8源码分析HashSet LinkedHashSet(八)

    一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...

  4. JDK1.8源码分析:可重入锁ReentrantLock和Condition的实现原理

    synchronized的用法和实现原理 synchronized实现线程同步的用法和实现原理 不足 synchronized在线程同步的使用方面,优点是使用简单,可以自动加锁和解锁,但是也存在一些不 ...

  5. 基于JDK1.8---HashMap源码分析

    基于JDK1.8-HashMap源码简要分析 HashMap继承关系 HashMap:根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不 ...

  6. JDK1.8源码分析之HashMap(一) (转)

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  7. jdk1.8 源码分析导图

    以下总结全部基于 jdk1.8,详细源码分析见 GitHub 链接:https://github.com/zchen96/jdk1.8-source-code-read 一.非并发 幕布导图链接:ht ...

  8. 【JUC】JDK1.8源码分析之ConcurrentHashMap

    一.前言 最近几天忙着做点别的东西,今天终于有时间分析源码了,看源码感觉很爽,并且发现ConcurrentHashMap在JDK1.8版本与之前的版本在并发控制上存在很大的差别,很有必要进行认真的分析 ...

  9. synchronousqueue场景_【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

  10. Java中ConcurrentHashMap底层实现原理(JDK1.8)源码分析2

    https://blog.csdn.net/programmer_at/article/details/79715177 https://blog.csdn.net/qq_41737716/categ ...

最新文章

  1. 95、Jenkins部署.net持续集成自动化测试环境
  2. matlab input函数学习
  3. Spring MVC 成员变量 request 线程安全问题的讨论
  4. jlink怎么调试linux程序_STM32开发板JLINK调试步骤
  5. fetch请求报错:
  6. _如何在各种Linux发行版中安装zip压缩与解压缩程序
  7. 程序员的绩效到底是应该衡量项目,还是改 Bug 量?
  8. python map函数_Python map()函数
  9. 基于51单片机的CC2541蓝牙透传模块的无线通信
  10. JavaScript之浏览器大战
  11. Python探路-多重继承
  12. java 判断数字是否连续,JAVA 判断是否连续字母或者数字
  13. PTC:能源互联网“双子星”,引领风电企业研发数字化变革
  14. github用户followers分析
  15. Unity-黑暗之魂复刻-动画控制器
  16. 【爬虫】爬取网易云热门歌曲歌曲信息-歌手、链接、歌手信息
  17. 接口参数加解密,代码无侵入这样做方便多了
  18. win 10网信政府版 无法登录微软账号
  19. tiptop gp 3.0开发流程
  20. 任志强是出演“ 黄世仁”的最佳人选?

热门文章

  1. 在html中写响应式布局的代码,CSS实现响应式布局的方法
  2. VR/AR的需求和前景
  3. 阿里云服务器常用配置收费1核2G/2核4G/4核8G/8核16G多配置
  4. 【高等数学】(底数>1和底数<1的)幂函数的图像
  5. 《机器学习实战》学习笔记(四):基于概率论的分类方法 - 朴素贝叶斯
  6. 数字调制解调—2ASK
  7. 国内dns服务器故障致大量网站无法访问,中国电信114.114.114.114DNS故障 大量网站域名无法解析...
  8. 锂离子电池电量计原理概述
  9. MpAndroidChart饼图
  10. 阿里云共享型云服务器与独享型云服务器有什么区别?如何选择?