多线程编程-- part 8 CyclicBarrier
CyclicBarrier简介
cuclicBarrier允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。因为该barrier在释放等待线程后可以重用,所以称它为循环的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数据结构

可见其包含的对象:
(1)parties:定义多少个等待线程可以启动屏障
(2)count:处于等待状态的线程数量
(3)lock:是ReentrantLock独占锁
(4)trip:condition条件控制,控制线程的等待和激活
(5)barrierCommand:表示当parties个处于等待状态的线程到达屏障时,要执行的动作
(6)generation:记录线程属于那一代,当有parties个线程到达barrier时,generation会被换代
CyclicBarrier核心函数
1.构造函数
(1)CyclicBarrier(int parties), CyclicBarrier(int parties, Runnable barrierAction)
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个线程,都调用await()等待,这些线程都到达屏障,继续往后执行
public class testHello {
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();
}
}
}
}

(2)新建5个线程,都调用await()等待,这些线程都到达屏障,执行runnable中定义的任务
public class testHello {
private static int SIZE = 5;
private static CyclicBarrier cb;
public static void main(String[] args) {
cb = new CyclicBarrier(SIZE, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " runnable start");
}
});
// 新建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();
}
}
}
}

可以看出当parties个线程到达屏障时,会执行屏障的Runable定义的任务
多线程编程-- part 8 CyclicBarrier的更多相关文章
- java核心-多线程-Java多线程编程涉及到包、类
Java有关多线程编程设计的类主要涉及两个包java.lang和java.util.concurrent两个包 java.lang包,主要是线程基础类 <1>Thread <2> ...
- Java多线程编程实战指南(核心篇)读书笔记(三)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76686044冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程(5)--线程间通信
一.等待与通知 某些情况下,程序要执行的操作需要满足一定的条件(下文统一将其称之为保护条件)才能执行.在单线程编程中,我们可以使用轮询的方式来实现,即频繁地判断是否满足保护条件,若不满足则继续判断 ...
- Web Worker javascript多线程编程(一)
什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...
- Web Worker javascript多线程编程(二)
Web Worker javascript多线程编程(一)中提到有两种Web Worker:专用线程dedicated web worker,以及共享线程shared web worker.不过主要讲 ...
- windows多线程编程实现 简单(1)
内容:实现win32下的最基本多线程编程 使用函数: #CreateThread# 创建线程 HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpT ...
- Rust语言的多线程编程
我写这篇短文的时候,正值Rust1.0发布不久,严格来说这是一门兼具C语言的执行效率和Java的开发效率的强大语言,它的所有权机制竟然让你无法写出线程不安全的代码,它是一门可以用来写操作系统的系统级语 ...
- windows多线程编程星球(一)
以前在学校的时候,多线程这一部分是属于那种充满好奇但是又感觉很难掌握的部分.原因嘛我觉得是这玩意儿和编程语言无关,主要和操作系统的有关,所以这部分内容主要出现在讲原理的操作系统书的某一章,看完原理是懂 ...
- Java多线程编程核心技术---学习分享
继承Thread类实现多线程 public class MyThread extends Thread { @Override public void run() { super.run(); Sys ...
随机推荐
- maven 打jar包和war包
( 最简单的:修改pom.xml文件: <packaging>war</packaging>(如果是jar,则是&l ...
- 阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_7 获取Servlet原生的API
控制器里面获取常用的request对象或者Response对象. 想拿到谁, 就在参数上写上谁 再获取servletConetext并输出
- ELK 日志平台构建
elastic中文社区 https://elasticsearch.cn/ 完整参考 ELK实时日志分析平台环境部署--完整记录 https://www.cnblogs.com/kevingrace/ ...
- mysql双主架构
注意:最好不要用innodedb来同步数据库,要用databus来同步数据库,数据量大要用上mycat中间件 Mysql主主同步环境部署: centos 7.4 三台云主机: mysql1 :10.1 ...
- Data - 【转】数据统计、数据挖掘、大数据、OLAP的区别
原文链接 数据分析 数据分析是一个大的概念,理论上任何对数据进行计算.处理从而得出一些有意义的结论的过程,都叫数据分析. 从数据本身的复杂程度.以及对数据进行处理的复杂度和深度来看,可以把数据分析分为 ...
- JavaEE_Test2_Servlet
package servlet; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.P ...
- 【HANA系列】【第三篇】SAP HANA XS的JavaScript安全事项
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第三篇]SAP HANA XS ...
- SQL优化手段
一.建立索引 要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引(order by desc会非常影响效率). 二.避免在建立索引的字段进行计算操作. 三.使用预编 ...
- 贡献python prim多源最短路搜索算法 numba加速方法的demo和总结
1.测试两个算法 #coding:utf-8 import time import numba import numpy as np ''' 使用numba加速总结, (1).在数值计算比如int f ...
- subprocess.call(cmd, shell=True)
1.使用方法 subprocess.call() 执行由参数提供的命令. 我们可以用数组作为参数运行命令,也可以用字符串作为参数运行命令(通过设置参数shell=True) 注意,参数shell默认为 ...