共计 1944 个字符,预计需要花费 5 分钟才能阅读完成。
前言
本篇文章的代码示例已放到 github 上,Git 地址为:advance(记录每一个学习过程),大家把代码下载下来之后,全局搜寻一些要害代码,即可找到该文章的源码。
大家感觉有用的话,麻烦点个 star👍再走呗!
应用场景
设想一个这样的场景,咱们在打王者光荣 / 英雄联盟的时候,都会有一个匹配机制,须要 10 集体都加载实现后,大家能力一起进入游戏,不然会呈现大家进入游戏的工夫不统一的状况,这个时候就能够应用 CyclicBarrier 来实现。
基本原理
应用 CyclicBarrier 的线程被叫做参与方,它的外部保护了一个显式锁。参与方只须要执行 await() 就能够参加期待,此时这些线程会被暂停。当最初一个线程执行 await() 办法后,其余被暂停的线程都会被唤醒,而最初一个线程不会被暂停。
罕用办法
// 结构器,定义参加的线程数
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
// 结构器,能够传入跳栅后须要执行的线程
public CyclicBarrier(int parties, Runnable barrierAction);
// 将屏障重置为其初始状态
void reset()
// 进行期待
int await()
// 进行期待,同时具备超时工夫
public int await(long timeout, TimeUnit unit)
应用示例
定义玩家运行程序
public class CyclicBarrierRunnable implements Runnable{
private CyclicBarrier cyclicBarrier;
private int number;
public CyclicBarrierRunnable(CyclicBarrier cyclicBarrier, int number) {
this.cyclicBarrier = cyclicBarrier;
this.number = number;
}
@Override
public void run() {System.out.println("玩家" + number + "号正在加载游戏...");
try {TimeUnit.SECONDS.sleep(2);
cyclicBarrier.await();} catch (Exception e) {System.out.println("线程执行呈现问题");
}
System.out.println("玩家" + number + "号加载实现。");
}
}
定义主程序
public class Main {public static void main(String[] args) throws InterruptedException, BrokenBarrierException {CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
// 获取参与方的总数
System.out.println("参与方的总数为:" + cyclicBarrier.getParties());
// 获取此时期待的线程数
System.out.println("此时期待的线程数为:" + cyclicBarrier.getNumberWaiting());
for (int i = 0; i < 10; i++){CyclicBarrierRunnable runnable = new CyclicBarrierRunnable(cyclicBarrier, i);
new Thread(runnable).start();}
}
}
运行后果
执行阐明
主线程每隔 2 秒会启动一个子线程执行,子线程打印“筹备执行”后,会调用 await() 办法进行期待,从后果咱们能够看出:当最初一个 CyclicBarrier.await() 办法被执行后,所有的期待线程同时被唤醒,同时开始执行。
外部原理
CyclicBarrier 外部应用了一个条件变量 trip 来实现期待 / 告诉。
CyclicBarrier 外部实现应用了分代的概念用于示意 CyclicBarrier 实例是能够重复使用的。
除最初一个线程外的任何一个参与方都相当于一个期待线程,这些线程所应用的爱护条件是:“以后分代内,尚未执行 await 办法的参与方个数为 0”。await() 办法每被执行一次,相应实例的 parties 值会缩小 1. 最初一个线程相当于告诉线程,它执行 await() 会使相应实例的 parties 的值变为 0,此线程会先执行 barrierAction.run(),而后再执行 trip.signalAll() 来唤醒所有期待线程。
注意事项
- 应用 reset() 办法将屏障置为初始状态时,如果所有参与者目前都在屏障处期待,则将他们唤醒,同时抛出一个 BrokenBarrierException 异样