CyclicBarrier类在性能测试中应用
CyclicBarrier
也叫同步屏障,在JDK1.5
被引入,可以让一组线程达到一个屏障时被阻塞,直到最后一个线程达到屏障时,所以被阻塞的线程才能继续执行。在执行很多个任务,但是这些任务中间某个节点需要等到其他任务都执行到固定的节点才能继续进行,先到达的线程会一直等待所有线程到达这个节点。在性能测试中,经常会遇到N多个用户同时在线的场景,一般处理起来都是先让这N多个用户登录,然后保持登录状态,然后去并发请求。这个场景下CyclicBarrier
就能完美解决我们的需求。
基本介绍
CyclicBarrier
类常用的构造方法有两个:1、只有一个int
类型的参数,表示参加等待的线程数,这一点跟CountDownLatch
类一样;2、构造方法多了一个Runnable
参数,这个表示所有线程都到达等待节点后执行的线程任务,网上大多数用赛跑的发令枪做比喻,很形象。当所有线程都到达准备好之后,发令枪就响了。
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and which
* will execute the given barrier action when the barrier is tripped,
* performed by the last thread entering the barrier.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @param barrierAction the command to execute when the barrier is
* tripped, or {@code null} if there is no action
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
重要方法
使用方法比较简单,构造方法完成后,之后一个方法await()
,这个方法用来表示到达节点后开始等待其他线程到达,同样的,还有一个重载方法,增加了超时设置,两个参数:1、时间;2、时间单位。如果该方法报了超时异常,那么其他等待线程到达这个方法后会报BrokenBarrierException
这个异常。由于CyclicBarrier
对象的await()
方法在同一线程中是可以多次调用的,相当于任务分成了很多阶段,一旦某一个线程的某一个任务阶段报错,会导致其他线程同样的任务阶段都报错,进而可能导致所有现成任务报错失败。
如果当前调用是最后一个调用,则唤醒所有其它的线程的等待并且如果在构造CyclicBarrier
时指定了action
,当前线程会去执行该action
,然后该方法返回该线程调用await
的次序(getParties()-1说明该线程是第一个调用await的,0说明该线程是最后一个执行await的),接着该线程继续执行await
后的代码;如果该调用不是最后一个调用,则阻塞等待;如果等待过程中,当前线程被中断,则抛出InterruptedException
;如果等待过程中,其它等待的线程被中断,或者其它线程等待超时,或者该barrier
被reset
,或者当前线程在执行barrier
构造时注册的action
时因为抛出异常而失败,则抛出BrokenBarrierException
。
reset()
该方法会将该barrier
重置为它的初始状态,并使得所有对该barrier
的await
调用抛出BrokenBarrierException
。
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
实践
下面是我写的一个测试Demo
,第一个线程我估计写了1秒的等待,出发超时报错的。
public static void main(String[] args) {
CyclicBarrier very_good = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
logger.warn("very good");
}
});
new Thread(() -> {
logger.info("111111111");
try {
very_good.await(1, TimeUnit.SECONDS);
sleep(1);
very_good.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
sleep(2);
logger.info("222222222");
try {
very_good.await();
very_good.await();
// very_good.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
testOver();
}
如果想实验Runnable
参数的调用的话,可以放开超时,控制台输出如下:
INFO-> 当前用户:fv,IP:192.168.0.107,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.6
INFO-> 111111111
INFO-> 222222222
WARN-> very good
WARN-> very good
Process finished with exit code 0
- Android监听自身卸载,弹出用户反馈调查
- Spring Boot 1.5.x新特性:动态修改日志级别
- XMPP客户端库Smack 4.0.6版开发之二
- Spring Cloud实战小贴士:版本依赖关系
- 如何优雅的用Python做接口自动化测试
- 忘记oracle的sys用户密码怎么修改以及Oracle 11g 默认用户名和密码
- hibernate链接数据库链接池c3p0配置
- Oracle中session和processes的设置
- ssh相关原理学习与常见错误总结
- PyQt5 GUI应用程序工具包入门(1)
- grpc部署初体验
- Java中的ReentrantLock和synchronized两种锁机制的对比
- 用Python从零开始创建区块链
- 基于 Python 的僵尸网络将 Linux 机器变成挖矿机器人
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- jQuery 元素操作
- jQuery 文本属性值
- jQuery 属性操作
- 方老师聊Nginx知识点
- 《Java从入门到失业》第五章:继承与多态(5.1-5.7):继承
- 【赵渝强老师】Flink的DataSet算子
- Node.js 案发现场揭秘 —— 文件句柄泄露导致进程假死
- nacos配置问题
- Chevereto 配合 Picgo 打造个人相册/图床解决方案
- 从提高 Elasticsearch 搜索体验说开去......
- 又一个奇葩要求,Python是如何将“中文”转“拼音”的?
- localStorage中怎么存对象?
- vue中sessionStorage的使用
- 别再问我 Python 怎么识别数字验证码了!
- Java 多线程设计模式 —— Single Threaded Execution