JUC锁-10之 CyclicBarrier原理和示例
来源:互联网 发布:maxwell软件渲染 编辑:程序博客网 时间:2024/06/03 20:08
0.本文目录
- 本文目录
- 开篇明志
- CyclicBarrier简介
- CyclicBarrier函数列表
- CyclicBarrier数据结构
- CyclicBarrier源码分析基于JDK170_40
- 构造函数
- 等待函数
- CyclicBarrier的使用示例
1.开篇明志
本章介绍JUC包中的CyclicBarrier锁。
2.CyclicBarrier简介
CyclicBarrier
是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
注意比较CountDownLatch
和CyclicBarrier
:
- CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
- CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
3.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()//将屏障重置为其初始状态。
4.CyclicBarrier数据结构
CyclicBarrier的UML类图如下:
CyclicBarrier是包含了”ReentrantLock对象lock”和”Condition对象trip”,它是通过独占锁实现的。下面通过源码去分析到底是如何实现的。
5.CyclicBarrier源码分析(基于JDK1.7.0_40)
CyclicBarrier.java 点击这里
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者之一发生,当前线程才继续执行。
- generation是CyclicBarrier的一个成员遍历,它的定义如下:
private Generation generation = new Generation();private static class Generation { boolean broken = false;}
在CyclicBarrier中,同一批的线程属于同一代,即同一个Generation;CyclicBarrier中通过generation对象,记录属于哪一代。
当有parties个线程到达barrier,generation就会被更新换代。
- 如果当前线程被中断,即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上所有的等待线程。
- 将“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的值。
- 在for(;;)循环中。timed是用来表示当前是不是“超时等待”线程。如果不是,则通过trip.await()进行等待;否则,调用awaitNanos()进行超时等待。
5.CyclicBarrier的使用示例
示例1
新建5个线程,这5个线程达到一定的条件时,它们才继续往后运行。
import java.util.concurrent.CyclicBarrier;import java.util.concurrent.BrokenBarrierException;public class CyclicBarrierTest1 { private static int SIZE = 5; private static CyclicBarrier cb; public static void main(String[] args) { cb = new CyclicBarrier(SIZE); // 新建5个任务 for(int i=0; i<SIZE; i++) new InnerThread().start(); } static class InnerThread extends Thread{ public void run() { try { System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier."); // 将cb的参与者数量加1 cb.await(); // cb的参与者数量等于5时,才继续往后执行 System.out.println(Thread.currentThread().getName() + " continued."); } catch (BrokenBarrierException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }}
运行结果:
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个线程达到一定的条件时,执行某项任务。
import java.util.concurrent.CyclicBarrier;import java.util.concurrent.BrokenBarrierException;public class CyclicBarrierTest2 { private static int SIZE = 5; private static CyclicBarrier cb; public static void main(String[] args) { cb = new CyclicBarrier(SIZE, new Runnable () { public void run() { System.out.println("CyclicBarrier's parties is: "+ cb.getParties()); } }); // 新建5个任务 for(int i=0; i<SIZE; i++) new InnerThread().start(); } static class InnerThread extends Thread{ public void run() { try { System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier."); // 将cb的参与者数量加1 cb.await(); // cb的参与者数量等于5时,才继续往后执行 System.out.println(Thread.currentThread().getName() + " continued."); } catch (BrokenBarrierException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }}
运行结果:
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: 5Thread-0 continued.Thread-4 continued.Thread-2 continued.Thread-3 continued.Thread-1 continued.
结果说明:主线程中新建了5个线程,所有的这些线程都调用cb.await()等待。所有这些线程一直等待,直到cb中所有线程都达到barrier时,执行新建cb时注册的Runnable任务。
- JUC锁-10之 CyclicBarrier原理和示例
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例 (r)
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
- JUC锁-09之 CountDownLatch原理和示例
- JUC锁-11之 Semaphore信号量的原理和示例
- java CyclicBarrier原理和示例
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例 (r)
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例 (r)
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
- 12.JUC 锁- CyclicBarrier
- JUC系列之CyclicBarrier/CountDownLatch/Semaphore
- JUC--CyclicBarrier的用法
- Java并发之CyclicBarrier示例
- tensorflow 滑动平均模型 ExponentialMovingAverage
- android上调试H5小工具
- 内部类
- CentOS7 使用 yum 安装 mysql 遇到的一个问题
- spring AspectJ基于注解报错问题以及基于xml后置通知和抛出异常通知报错问题
- JUC锁-10之 CyclicBarrier原理和示例
- JUC锁-11之 Semaphore信号量的原理和示例
- CentOS7配置AMP安装配置(Apache + MySQL + PHP)
- CentOS7 VNC SERVER的安装配置
- Python:数组中的逆序对
- 利用Excel和ren命令批量重命名文件技巧
- 表格中点击行触发checkbox选中或取消和取值
- ROS同步设置
- 树莓派时间设置