前言

本篇文章的代码示例已放到 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()来唤醒所有期待线程。

注意事项

  1. 应用reset()办法将屏障置为初始状态时,如果所有参与者目前都在屏障处期待,则将他们唤醒,同时抛出一个BrokenBarrierException异样