AQS之CyclicBarrier源码解读

首先准备一个可以跑的Demo

@Slf4j
public class CyclicBarrierExample{
    //1、实例化一个CycleBarrier
    private static CyclicBarrier barrier = new CyclicBarrier(5);

    public static void main(String[] args) throws Exception {
        //2、创建线程池
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            final int threadNum = i;
            Thread.sleep(1000);
            executor.execute(() -> {
                try {
                    race(threadNum);
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        //4、关闭线程池
        executor.shutdown();
    }

    private static void race(int threadNum) throws Exception {
        Thread.sleep(1000);
        log.info("{} is ready", threadNum);
        //3、堵塞等待任务达到给定参数的数量后释放
        barrier.await();
        log.info("{} continue", threadNum);
    }
}

先说一个不是非常恰当的比喻来说明下我对CyclicBarrier的理解

 有一根管子里面有一个可以进行自我修复的薄膜,薄膜能承受最大的重量为parties个单位,然后我们向管子里面扔石子,一个石子的重量为1一个单位,当石子遇到薄膜的时候被堵在薄膜上不能够继续往下下落,直到石子的数量大于parties个单位,于是可以冲破薄膜往下继续下落,薄膜在等所有石子通过之后立即自我修复然后等待下一批石子。

我们通过CyclicBarrierExample去简单猜测一下源码:创建的时候我们给定了一个参数parties=5,在执行线程任务的时候每一个任务执行之前内部有一个计数器会加1,然后该线程被堵塞,直到内部的计数器等于我们传递的参数parties的时候,被堵塞的任务被执行。

下面验证下我们的猜测是否正确

我们先看看CyclicBarrier的构造函数

public CyclicBarrier(int parties) {
        this(parties, null);
    }
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

这里可以看到初始化CyclicBarrier的时候一共填充了内部的三个字段:

1、parties:这里就是我们刚刚讨论的parties

2、count:这里初始化的值等于我们的parties,因此每次传入一个线程是执行减1操作而非我们刚刚所说的加1,不过原理是一样的。

3、barrierCommand:这里作为一个可选的参数,就是在屏障被打破的时候,也就是线程执行任务之前执行的操作。

 

我们继续看看序号3的await方法做了什么

 public int await() throws InterruptedException, BrokenBarrierException {
        try {
            //第二个参数是超时时间,传入空参的时候表示不设置超时时间
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

继续看dowait,是CyclicBarrier的核心方法

 /**
     * Main barrier code, covering the various policies.
     */
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        //堵塞
        lock.lock();
        try {
            //得到当前线程的批次(代)信息
            final Generation g = generation;
            //如果当前批次已经执行过了
            if (g.broken)
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            //计数器(还记得刚刚解释的count吗)减1
            int index = --count;
            //如果减1之后为0,说明线程已经累计到一定的数量,可以冲破屏障了
            if (index == 0) {  
                //是否可以执行线程任务
                boolean ranAction = false;
                try {
                    //冲破屏障后需要执行的操作,入参,上面解释过,我们Demo传入的是null
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    //标记可以执行线程任务了
                    ranAction = true;
                    //直接进入到下一代,重新初始化参数
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            //都走到这里了说明count还不为0,于是来一个循环堵塞在这
            for (;;) {
                try {
                    //看看设置了超时没有,如果没有就继续等着吧
                    if (!timed)
                        trip.await();
                    //如果设置了超时时间那就等到时间结束
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        Thread.currentThread().interrupt();
                    }
                }
                
                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            //无论是否发生了异常,先把锁释放掉
            lock.unlock();
        }
    }

这里源码逻辑还是很清楚的,没有什么特别难的地方。

在读源码的时候发现一个细节,就是CyclicBarrier不像CountDownLatch一样定义了一个Sync继承于AbstractQueuedSynchronizer类,那CyclicBarrier为什么也号称是基于AQS的呢?ReentrantLock里面有一个公平锁FairSync和非公平锁UnFairSync继承于Sync,可以发现其声明了一个ReentrantLock字段,而CyclicBarrier实现的是空参的ReentrantLock默认是非公平锁。所以CyclicBarrier也可以说是基于AQS的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值