Java多线程/并发23、循环屏障CyclicBarrier

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/soonfly/article/details/71188746

CyclicBarrier 直译叫循环屏障或循环分界点。让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。

打个比方,F1大奖赛共5支车队参赛。第一站:上海站。比赛马上要开始了,各个车队都在准备,但是先到达起跑线准备就绪的车队没法开始比赛,必须等待。只有当5支车队都到达起跑线准备就绪后,裁判才可以发起比赛。这5个车队到达起跑线准备好后才能比赛的规则就是CyclicBarrier。比完上海站,下一步进入摩纳哥站,这规则又可以循环使用,还是5支车队到达才可以进行比赛。我们预设的5就是屏障点。

CyclicBarrier主要的方法是:await()。await()方法一旦被调用,当前线程就被阻塞,计数器便会减少1。当计数减至0时,所有在此 CyclicBarrier的线程的阻塞解除,开始运行。在这之后,如果再次调用 await() 方法,计数就又会变成 N-1,新一轮阻塞等待重新开始,这便是Cyclic循环的含义所在。

假设有个线程中途崩溃了,其它线程就会永远等待吗?
为解决这个问题,CyclicBarrier.await() 方法会抛出一个独有的 BrokenBarrierException异常。这个异常发生在当某个线程在等待本 CyclicBarrier 时被中断、超时、重置时,其它在这个CyclicBarrier 上等待的线程便会收到 BrokenBarrierException。意思就是说,同志们,别等了,有个小伙伴已经挂了,你们该干嘛干嘛去。

CyclicBarrier常用方法:

  • await() 阻塞当前线程,返回表示当前线程是第几个到达这个 Barrier 的线程。
  • getNumberWaiting()返回当前已经在等待的队友线程数
  • isBroken() 查询屏障点是否达到突破
  • reset() 重置屏障
public class CyclicbarrierDemo {

    public static void main(String[] args) {

        /*预设参赛的车队*/
        final CyclicBarrier cbr=new CyclicBarrier(5);
        List<String> teams=new ArrayList<String>();
        teams.add("法拉利");
        teams.add("麦凯伦");
        teams.add("雷诺");
        teams.add("红牛");
        teams.add("梅赛德斯");
        /*为每个车队开启一个线程*/
        for (String team : teams) {
            final String tempteam=team;
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(6));
                        System.out.println("共"+cbr.getParties()+"辆车参赛,"+tempteam+"车队第"+(cbr.getNumberWaiting()+1)+"个准备就绪");
                        }
                        /*等待5参车都就绪后,才能往下运行*/
                        cbr.await();
                        System.out.println(tempteam+"车队出发");
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

输出:
这里写图片描述
有时会出现上面这种情况。
因此getNumberWaiting在获取就绪的线程数量时只做为参考值。因为在wait()前的时间段内,可以有多个线程同得获取到相同的就绪数量。

另外,千万不要试图写出这样的同步代码:

synchronized (this) { 
System.out.println("共"+cbr.getParties()+"辆车参赛,"+tempteam+"车队第"+(cbr.getNumberWaiting()+1)+"个准备就绪");
cbr.await();
}

将await()放在同步代码块或lock锁中
这样就会让线程死锁,因为await()等待,同步代码块就无法执行完,其它线程就无法进行代码块中调用wait等待,那么永远不可能达到屏障点,而让线程运行。

猜你喜欢

转载自blog.csdn.net/soonfly/article/details/71188746