小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。 本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
注:掘金潜水怪今日起开更 注:老后端不水图
前言
前文给大家介绍了CountDownLatch,本文给大家介绍一下姊妹篇CyclicBarrier。老规矩还是从介绍、方法、场景、原理四个角度方面开展学习,话不多说,马上开始。
一:介绍
官方介绍: 这是一种同步辅助,允许一组线程全部等待彼此到达公共屏障点。 CyclicBarriers在涉及固定大小的线程组的程序中很有用,必须偶尔互相等待。屏障被称为 Cyclic因为它可以在等待线程被释放后重新使用。CyclicBarrier 支持可选的 Runnable 命令 在每个屏障点运行一次,在parties中的最后一个线程 * 到达之后,但在任何线程被释放之前。这个cyclic对于在任何一方继续之前更新共享状态很有用。
翻译一下就是:
二:方法
CyclicBarrier公开方法主要有:
int getParties() //需要等待阻塞线程的个数
int await() //等待,直到所有各方都在此屏障上调用了await (中断,reset等方法)
boolean isBroken() //查询此屏障是否处于破坏状态
void reset() //将障碍重置为其初始状态。如果任何一方正在 * 当前等待屏障,他们将返回 * {@link BrokenBarrierException}。
int getNumberWaiting() //返回当前在障碍处等待的参与方数量
我举一个简单的例子,辅助大家对CyclicBarrier有一个深刻的认识。 小猪参加比赛,等所有小猪都到准备好后开始比赛,最后得到胜者。 代码:
/**
* @function 单任务执行
*/
@SneakyThrows
static void singleTask(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
System.out.println("猪子们都来比赛了");
new Thread(new Pig(cyclicBarrier),"A猪").start();
new Thread(new Pig(cyclicBarrier),"B猪").start();
new Thread(new Pig(cyclicBarrier),"C猪").start();
Thread.sleep(2000);
System.out.println("全部结束比赛");
}
/**
* 这是一群小猪猪
*/
class Pig implements Runnable{
private CyclicBarrier cyclicBarrier;
public Pig(CyclicBarrier cyclicBarrier1){
cyclicBarrier = cyclicBarrier1;
}
@Override
public void run() {
try {
Thread.sleep((int) (Math.random() * 1000));
// 猪猪到达门口
System.out.println(Thread.currentThread().getName() + "到达出发地点");
System.out.println("broken::" + cyclicBarrier.isBroken() + ",parties::" + cyclicBarrier.getParties() +
",waiting::" + cyclicBarrier.getNumberWaiting());
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "结束比赛。。");
}catch (BrokenBarrierException e){
System.out.println(Thread.currentThread().getName() +"::"+e.getClass());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
根据代码,当所有小猪猪到达出发地点之后 (都执行了await方法),CyclicBarrier条件满足,开闸放猪,最后结束比赛。代码中还分别打印了 broken是否开闸的状态、parties所有条件数目、waiting当前达到条件数目。看运行结果:
猪子们都来比赛了
C猪到达出发地点
broken::false,parties::3,waiting::0
A猪到达出发地点
broken::false,parties::3,waiting::1
B猪到达出发地点
broken::false,parties::3,waiting::2
B猪结束比赛。。
C猪结束比赛。。
A猪结束比赛。。
全部结束比赛
三:场景
CyclicBarrier跟CountDownLatch比较,增加了可循环使用的功能和自主重置条件功能,减少了countDown()功能,调用await()方法时会自动减一。可应用场景也更加强大,可以使用循环栅栏、栅栏条件手动重置等情况。
情况一:普通栅栏
可以参考上面小猪比赛的例子。
情况二:循环栅栏
由于小猪们比较多,场地有限,只能采取每组两个头猪的小组赛策略来进行比赛,但又不能针对每族比赛都修改一次代码(成本太高),便采用循环栅栏。 代码示例:
/**
* @function cyclicBarrier可以重复利用
* 1 反复使用
*/
@SneakyThrows
static void mulitTask(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("人员到齐,开始比赛");
}
});
System.out.println("猪子们都来比赛了");
new Thread(new Pig(cyclicBarrier),"A猪").start();
new Thread(new Pig(cyclicBarrier),"B猪").start();
Thread.sleep(2000);
new Thread(new Pig(cyclicBarrier),"C猪").start();
new Thread(new Pig(cyclicBarrier),"D猪").start();
Thread.sleep(2000);
new Thread(new Pig(cyclicBarrier),"E猪").start();
new Thread(new Pig(cyclicBarrier),"F猪").start();
}
每组放出两头小猪进行比赛,依次进行。 运行结果:
猪子们都来比赛了
B猪到达出发地点
broken::false,parties::2,waiting::0
A猪到达出发地点
broken::false,parties::2,waiting::1
人员到齐,开始比赛
A猪结束比赛。。
B猪结束比赛。。
C猪到达出发地点
broken::false,parties::2,waiting::0
D猪到达出发地点
broken::false,parties::2,waiting::1
人员到齐,开始比赛
D猪结束比赛。。
C猪结束比赛。。
E猪到达出发地点
broken::false,parties::2,waiting::0
F猪到达出发地点
broken::false,parties::2,waiting::1
人员到齐,开始比赛
F猪结束比赛。。
E猪结束比赛。。
分析结果,没两组进行一次比赛,CyclicBarrier进行了多次重复利用。
场景三:手动重置栅栏条件
小猪在比赛的过程当中,存在作弊抢跑等行为,为了遏制这一行为,有猪犯规后将被取消比赛资格,其他剩余猪猪继续比赛。 示例代码:
/**
* @function reset重置
* 1.reset后正在等待的线程会抛出异常 BrokenBarrierException
*/
@SneakyThrows
static void resetTask(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("通过cyclic后执行");
}
});
System.out.println("猪子们都来比赛了");
new Thread(new Pig(cyclicBarrier),"A猪").start();
new Thread(new Pig2(cyclicBarrier),"有猪犯规,重置比赛").start();
new Thread(new Pig(cyclicBarrier),"B猪").start();
new Thread(new Pig(cyclicBarrier),"C猪").start();
Thread.sleep(1000);
System.out.println("结束比赛");
}
/**
* 这是一群小猪猪
*/
class Pig implements Runnable{
private CyclicBarrier cyclicBarrier;
public Pig(CyclicBarrier cyclicBarrier1){
cyclicBarrier = cyclicBarrier1;
}
@Override
public void run() {
try {
Thread.sleep((int) (Math.random() * 1000));
// 猪猪到达门口
System.out.println(Thread.currentThread().getName() + "到达出发地点");
System.out.println("broken::" + cyclicBarrier.isBroken() + ",parties::" + cyclicBarrier.getParties() +
",waiting::" + cyclicBarrier.getNumberWaiting());
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "结束比赛。。");
}catch (BrokenBarrierException e){
System.out.println(Thread.currentThread().getName() +"犯规,取消比赛资格::"+e.getClass());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Pig2 implements Runnable{
private CyclicBarrier cyclicBarrier;
public Pig2(CyclicBarrier cyclicBarrier1){
cyclicBarrier = cyclicBarrier1;
}
@Override
public void run() {
try {
Thread.sleep((int) (Math.random() * 1000));
// 猪猪到达门口
cyclicBarrier.reset();
System.out.println(Thread.currentThread().getName() + "reset");
}catch (Exception e){
System.out.println(Thread.currentThread().getName() +"::"+e.getMessage());
}
}
}
查看运行结果:
猪子们都来比赛了
B猪到达出发地点
broken::false,parties::2,waiting::0
有猪犯规,重置比赛reset
B猪犯规,取消比赛资格::class java.util.concurrent.BrokenBarrierException
A猪到达出发地点
broken::false,parties::2,waiting::0
C猪到达出发地点
broken::false,parties::2,waiting::1
通过cyclic后执行
C猪结束比赛。。
A猪结束比赛。。
结束比赛
四:原理
CyclicBarrier底层采用了 ReentrantLock 保持操作同步,基本原理如下:
欢迎大家点赞评论!!!!