CyclicBarrier

cyclic 循环的 barrier 栅栏。
latch vs barrier
门闩,可以加一道,两道,三道。
而栅栏只有一道,并且把所有的线程都拦住了。

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

The barrier is called cyclic because it can be re-used after the waiting threads are released.


栅栏可以看成是拦羊
成员变量:
parties: 要拦住几只羊?
count:已经拦住了几只羊?
barrierCommand: 拦住了这么多羊之后,是通知农场主还是农场主夫人?
generation:这是第几次拦羊了?--不是,是这个栅栏是不是没用了,损坏了,只有一个false和一个true

generation里面只有一个bool值 broken


CyclicBarrier内部逻辑:

1. 创建栅栏,设置好parties
2. 在各个线程中设置barries.dowait()
3. 每调用一次dowait,barries.count--
    3.1 如果count=0,则进入下一个generation,通知所有正在park的线程(从condition等待队列转入syc同步队列)
    3.2 如果count!=0, 则trip.await(实际调用的是ConditionObject.await方法),将node从sync队列转入condition的等待队列,然后park线程。

4. 异常处理:
    4.1 如果拿到锁之后,g.broken= true了,那么也就意味着这个barrie损坏了,那么抛出异常,释放锁
    4.2 如果当前线程被中断了,或者操作中有异常,breakBarrier,打破栅栏,通知所有等待的线程

主要方法:dowait

private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final Generation g = generation;

  //一开始进来,barrier就是损坏的,直接unlock出去
        if (g.broken)
            throw new BrokenBarrierException();

 //如果线程被中断,那就打破栅栏,并通知其他所有的小伙伴------这里一直想不通,为什么要把他们绑定在一起?
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }

        int index = --count;
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
        //有两种情况会有返回值,都是正常情况,第一种就是正好是最后一个线程,然后generation顺利进入下一代,返回0
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        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 {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

      //这是第二种正常情况,也就是被通知的其他小伙伴,醒来之后,发现已经换代了,返回当前的index
      //但是这里也有一个问题,线程park了之后,index还是保留的原来的那个index呢?还是最新的呢?按道理来说应该是原来的,那么原来的又有什么意义呢?
            if (g != generation)
                return index;

    //超时打破
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

这里trip.await的排队都是用的Condition的排队等。那么condition是怎么处理的呢?
首先Condition实现了AQS,而AQS中Condition的子类,ConditionObject。
ConditionObject虽然沿用了Node来作为condition的队列,但是这个队列跟aqs的队列并不是同一个。
沿用网上的说法,aqs的叫同步等待队列,condition的叫条件等待队列。


conditionObject有这么多方法,但是实际实用的是这两个:
1. addConditionWaiter  条件等待队列增加节点
2. doSignal 从条件队列移除,移至同步等待队列

trip.await的时候:


1. 加入等待队列,释放掉aqs的锁,唤醒同步队列的next
2. 循环中判断是否在同步队列(这里涉及到signal的操作,signal之后,会把node从等待队列移动到同步队列)
3. 如果已经在同步队列,那么重新取锁
4. 如果等待队列还有next,那么清空等待队列中非condition的节点。
5. 中断处理

breakBarrier和nextGeneration中有调用signalAll


singnalAll

1.  从first开始,先判断是否取消
2. 将node加入sync队列(enq方法),并将condition等待队列的节点一个一个转换成Signal的节点,也就是waitStatus改成Signal
3. unpark线程

1.  为什么一个线程中断了,就必须打破barrier呢?
难道是因为它持有锁?释放锁不就可以了嘛?

2. breakBarrier 和 nextGeneration有什么区别?
breakBarrier是打破栅栏,直接损坏了,也就是打破了Cyclic这个循环的属性,以后不能再用了。
nextGeneration是进入下一个代,也就是可以循环使用的。

在doWait方法的一开始就判断了g.broken,如果是true,直接抛出异常,也就是说,如果barrier损坏了,那就没有等待这一说了。
而在死循环下,也就是await之后,先是判断了g.broken,这种情况就是异常后signal的情况,直接释放。
然后再判断了g = generation,也就是看是不是当前代,如果不是,直接返回index。

3. g!=generation时,index返回有意义嘛?
感觉没有意义。

4. cyclicBarrier 有先后顺序嘛?
有,但是不明显。除了最后一个先走,其他的羊 栅栏关上的时候,谁先来,那么栅栏打开的时候,谁就最先走。
栅栏关上,所有的羊都挂在等待队列里。
羊的数量达到了。
栅栏打开,先是最后一只羊开始跑出圈,然后其他拦住的羊分别从第一只羊开始,从等待队列里,移到同步队列里。
再依次跑出圈。
所以,拼团这种活动,最后一个最快,其次是第一个,再就是按照顺序一个一个来。

5. 为什么wait都要放在死循环里?

因为唤醒之后,如果没有在死循环里,那么方法就跳出去了。
不仅需要在死循环了,而且还要对唤醒后的业务重新操作。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值