AQS之CyclicBarrier

10 篇文章 1 订阅
6 篇文章 0 订阅

目录

await

总结


上篇文章介绍了CountDownLatch,这篇文章重点介绍CyclicBarrier。上篇文章里说过,CountDownLatch是基于AQS的共享模式实现的,而CyclicBarrier其实是基于ReentrantLock和Condition来实现的。从名称上来看CyclicBarrier就是可循环的栅栏,如果我们把CountDownLatch当成一个计数器,那CyclicBarrier就是可重复利用的计数器,它拥有重置的功能,而CountDownLatch减到0就没了。

下面开始分析CyclicBarrier的源码,先开看一下CyclicBarrier类中都有哪些重要属性

public class CyclicBarrier {
    /**
     * 这里把每次跳过栅栏用一个Generation的实例来表示,
     * 每次穿过栅栏Generation就会发生变化或者重置
     */
    private static class Generation {
        boolean broken = false;
    }

    /** 栅栏入口的锁*/
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition就是等待某个条件,这里是代表:CyclicBarrier要想穿过栅栏
     *  需要等待所有线程都到达了栅栏
     */
    private final Condition trip = lock.newCondition();
    /** 其实就是线程数 */
    private final int parties;
    /* 这个就是穿过栅栏前,需要执行的操作 */
    private final Runnable barrierCommand;
    /** 当前的Generation,注:这时候它的broken字段还是false */
    private Generation generation = new Generation();

    /**
     * count代表着还在等待,也就是未到达栅栏的线程
     * count的初始值为构造方法传入的parties,
     * 然后回递减,一直减到0
     */
    private int count;
    ............
}

await

CyclicBarrier最核心的就是await()方法

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            // 这里传入false代表不带有超时机制
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
    // CyclicBarrier核心代码
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        // 在操作之前,必先获取到栅栏的独占锁
        lock.lock();
        try {
            // 获取代表当前这一代的Generation实例
            final Generation g = generation;
            // 如果栅栏已经被打破,抛出异常
            if (g.broken)
                throw new BrokenBarrierException();
            // 判断线程中断状态。响应中断
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            // 我们说了count代表还没到达栅栏的线程,这里对count减1
            // 代表当前线程已经到达了栅栏。index是count递减后得到的值
            // 这个方法最终返回的也是这个index
            int index = --count;
            // 如果index==0,说明所有的线程都到达了栅栏
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        // 执行穿越栅栏前的操作
                        command.run();
                    ranAction = true;
                    // 这个方法很关键,会唤醒所有在栅栏上等待的线程,
                    // 更新Generation对象,重新开启新的一代
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // 如果当前线程是最后一个到达栅栏的,那么在上面if(index==0)这个分支里就返回了,
            // 如果当前线程到达栅栏的时候还有别的线程没有到达栅栏。就会执行这个for循环。
            for (;;) {
                try {
                    // 如果不支持超时机制调用condition的await()方法,
                    // 将当前线程加入到condition队列,然后挂起,
                    // 等着进入AQS等待(同步)队列,直到最后到达栅栏的那个线程,
                    // 执行condition的signalAll()方法唤醒所有在栅栏上等待的线程
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        // 如果支持超时机制。那么调用condition带有超时机制的await方法
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    // 如果条件成立,说明线程执行condition.await发生了中断,
                    // 那么,打破栅栏,抛出异常
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // 否则说明generation对象发生了变化
                        // 即最后一个到达栅栏的线程已经完成了await()
                        // 那么这个时候没必要再抛出interrupted异常
                        Thread.currentThread().interrupt();
                    }
                }
                // 如果栅栏已经被打破,抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
                // 最后一个到达栅栏的线程会执行nextGeneration更新generation对象,
                // 也就是生成新的一代,重置count,同时会唤醒所有其他await()的线程,
                // 这个时候,所有线程走到这里的时候,g != generation这个条件就是成立的,
                // 这个方法到这里就返回了
                if (g != generation)
                    return index;
                // 发现超时了,打破栅栏。抛出异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

看下这里面两个重要的方法nextGeneration()和breakBarrier()。

    // 最后一个到达栅栏的线程会调用这个方法唤醒其他await的线程。同时重新生成下一代
    private void nextGeneration() {
        // 唤醒所有通过condition.await进入等待的线程
        trip.signalAll();
        // 设置下一代的count的值
        count = parties;
        generation = new Generation();
    }
    // 打破栅栏
    private void breakBarrier() {
        // 设置generation对象的broken属性为true
        generation.broken = true;
        // 重置count
        count = parties;
        // 唤醒所有等待中的线程
        trip.signalAll();
    }

总结

CyclicBarrier源码相对CountDownLatch真是简单了不少,就是简单了利用了一下ReentrantLock+Condition组合。下面简单总结一下

  1. 通过构造方法new CyclicBarrier(N)给count属性赋值为N
  2. 每个线程来了之后先获取栅栏的锁,对count进行-1操作,代表当前线程已经到达了栅栏
  3. 调用Conditionawait方法将当前线程挂起,await会释放独占锁。每个线程都会对count减1
  4. 当count减为0的时候。说明最后一个线程到达了栅栏。由最后这个线程执行Condition.signalAll方法唤醒所有挂起的线程。
  5. 线程重新获取到锁后。发现所有线程都已经到达了栅栏,才能继续执行,CyclicBarrier.await方法返回。

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值