CyclicBarrier解读

来源:互联网 发布:nginx 隐藏源ip 编辑:程序博客网 时间:2024/06/05 23:02

CyclicBarrier能实现与CountDownLatch相同的功能,但是两者的实现上是不一样的,而且CountDownLatch每次使用完再继续使用就会有问题,而CyclicBarrier不一样,可以一直使用,下面我们来分析一下CyclicBarrier的代码。

CyclicBarrier的代码中并没有定义自己的同步器类,他是基于ReentrantLock和Condition来实现的    /** The lock for guarding barrier entry */ 

private final ReentrantLock lock = new ReentrantLock(); 

/** Condition to wait on until tripped */

private final Condition trip = lock.newCondition();


我们这样子创建CyclicBarrier 对象,

private final static CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier( 3);当然也可以传入一个实现Runnable接口的对象,每次任务完成时都显示调用这个Runnable任务。CYCLIC_BARRIER 最终调用的构造方法如下:


    public CyclicBarrier(int parties, Runnable barrierAction) {

        if (parties <= 0) throw new IllegalArgumentException();

        this.parties = parties;

        this.count = parties;

        this.barrierCommand = barrierAction;

    }

我们传入的数量3被赋值给了CyclicBarrier中的parties和count 属性,两个属性的定义如下:

private final int parties;

private int count;

注意parties属性是final的,一旦初始化完成就不允许再更改了。

接下来我们看看CYCLIC_BARRIER.await();的实现,最终调用的是CyclicBarrier中的dowait()方法


    /**

     * 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();

            }


           int index = --count;

           if (index == 0) { // tripped

               boolean ranAction = false;

               try {

                   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();


                if (g != generation)

                    return index;


                if (timed && nanos <= 0L) {

                    breakBarrier();

                    throw new TimeoutException();

                }

            }

        } finally {

            lock.unlock();

        }

    }


代码主要的逻辑就是先赋值

int index = --count;count中存放的就是我们传入的参数3,前两次--count赋值给index后肯定是大于0的,往下走就会进入一个自旋的方法,最后调用到Condition对象的await()方法。

在这里我们要先来看看这个ReentrantLock中的这个Condition是个什么东东。

    public Condition newCondition() {

        return sync.newCondition();

    }

翻开源码看到这个newCondition方法是调用同步器的newCondition方法创建的,同步器中的newCondition代码如下:

       final ConditionObject newCondition() {

            return new ConditionObject();

        }

这个ConditionObject对象实际上是在AQS中定义的一个内部类。ConditionObject对象的await方法如下所示:


        public final void await() throws InterruptedException {

            if (Thread.interrupted())

                throw new InterruptedException();

            Node node = addConditionWaiter();

            int savedState = fullyRelease(node);

            int interruptMode = 0;

            while (!isOnSyncQueue(node)) {

                LockSupport.park(this);

                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

                    break;

            }

            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

                interruptMode = REINTERRUPT;

            if (node.nextWaiter != null) // clean up if cancelled

                unlinkCancelledWaiters();

            if (interruptMode != 0)

                reportInterruptAfterWait(interruptMode);

        }

阅读代码会发现ConditionObject对象的中也维护了一个队列,调用CYCLIC_BARRIER.await();方法的线程会阻塞在await这个方法之中,阻塞之前调用 fullyRelease(node);方法释放了ReentrantLock的锁。

当最后一个线程最后一个--count赋值index为0(int index = --count;)的时候,会走dowait方法的一下这块逻辑

           if (index == 0) { // tripped

               boolean ranAction = false;

               try {

                   final Runnable command = barrierCommand;

                   if (command != null)

                       command.run();

                   ranAction = true;

                   nextGeneration();

                   return 0;

               } finally {

                   if (!ranAction)

                       breakBarrier();

               }

           }

nextGeneration()方法的代码如下:

    private void nextGeneration() {

        // signal completion of last generation

        trip.signalAll();

        // set up next generation

        count = parties;

        generation = new Generation();

    }

调用Condition对象trip的signalAll();方法唤醒阻塞在ConditionObject对象中的线程中,大家就都会执行过,

然后将count用final的parties变量重新赋值,这样count 就恢复了,从而这个CyclicBarrier对象以后还是可以继续使用。

  



0 0
原创粉丝点击