CyclicBarrier源码分析-java8

1.特点分析

  • CyclicBarrier是一种同步机制,它可以使得一组线程在同一个障碍点进行等待
  • CyclicBarriers 可以通过重置计数器从而重新使用
  • CyclicBarrier支持一个可选的Runnable命令(实例化构造函数中的参数),该命令在最后一个线程到达后,但在任何线程被释放之前被执行。这一命令在barrier处只会被执行一次,且由最后到达的线程完成。这种屏障行为对于在任何一方继续之前更新共享状态都很有用。
  • all-or-none破损模型:如果一个线程因为中断(or执行过程的失败,超时等)过早的离开了barrier点,那么等待在barrier点的其他所有线程也会在同一时间因为BrokenBarrierException或者InterruptedException异常而离开barrier。
  • 内存一致性影响:
    • 线程在调用await()方法之前的行为要优先于barrier action中的任何行为。
    • barrier action成功返回这一行为要优先于所有其他等待线程await()后的行为。
  • 使用可重入锁ReentrantLock

2.部分域和方法的介绍

  • private static class Generation 类
    • 只有一个域boolean broken=false;
    • 用于标识:当前barrier是否处于broken状态。
  • private final ReentrantLock lock = new ReentrantLock();
    barrier入口的保护锁
  • private final Condition trip = lock.newCondition();
    trip前的等待条件
  • private final int parties;
    同时使用这个barrier的线程个数
  • private final Runnable barrierCommand;
    当trip时,barrier定义的屏障操作.
  • private Generation generation = new Generation();
    当前generation
  • private int count;
    • 等待线程数。每一个generation上,count值都会从parties降为0。
    • 针对每一个新的generation或者当前barrier被broken时,count值都会被reset。
  • 2个构造方法
    • public CyclicBarrier(int parties)
    • public CyclicBarrier(int parties, Runnable barrierAction)
  • private int dowait(boolean timed, long nanos)方法
    • 此方法是barrier的主要代码,里面包括类很多实现上的策略.
    • await()方法直接调用本方法。
    • 执行过程:
      这里写图片描述
  • 要加锁的方法
    dowait(),isBroken(),reset(),getNumberWaiting()。

3.源码分析

    package sourcecode.analysis;

    /**
     * @Author: cxh
     * @CreateTime: 18/4/15 11:11
     * @ProjectName: JavaBaseTest
     */
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;

    /**
     * CyclicBarrier是一种同步机制,它可以使得一组线程在同一个障碍点进行等待.在涉及到固定个数的线程组且有时必须互相等待时,
     * 则CyclicBarriers此时就变得很有用.CyclicBarrier前缀有cyclic,是因为在线程释放后,CyclicBarriers可以通过重置
     * 计数器从而重新使用.
     *
     * CyclicBarrier支持一个可选的Runnable命令,该命令在最后一个线程到达后,但在任何线程被释放之前,这一命令在barrier只会被执行一次,且由最后
     * 到达的线程完成。这种屏障行为对于在任何一方继续之前更新共享状态都很有用。
     * 使用样例: 在并行分解设计中如何使用一个barrier,请看如下样例:
     *
     * class Solver {
     *   final int N;
     *   final float[][] data;
     *   final CyclicBarrier barrier;
     *
     *   class Worker implements Runnable {
     *     int myRow;
     *     Worker(int row) { myRow = row; }
     *     public void run() {
     *       while (!done()) {
     *         processRow(myRow);
     *
     *         try {
     *           barrier.await();
     *         } catch (InterruptedException ex) {
     *           return;
     *         } catch (BrokenBarrierException ex) {
     *           return;
     *         }
     *       }
     *     }
     *   }
     *
     *   public Solver(float[][] matrix) {
     *     data = matrix;
     *     N = matrix.length;
     *     Runnable barrierAction =
     *       new Runnable() { public void run() { mergeRows(...); }};
     *     barrier = new CyclicBarrier(N, barrierAction);
     *
     *     List<Thread> threads = new ArrayList<Thread>(N);
     *     for (int i = 0; i < N; i++) {
     *       Thread thread = new Thread(new Worker(i));
     *       threads.add(thread);
     *       thread.start();
     *     }
     *
     *     // wait until done
     *     for (Thread thread : threads)
     *       thread.join();
     *   }
     * }
     *
     * 此例中,每一个worker线程会处理矩阵中的一行数据,然后在barrier处等待直到所有的行被处理完.当所有的行都被处理完成后,
     * 然后barrier行为被执行,实现合并行.如果合并时发现已经有线程完成了合并操作则每一个worker线程被终止,且直接返回true.
     *
     * 如果barrier行为在执行时,并不依赖于挂起的线程组,则线程组中的任意线程在被释放后,都可以去执行barrier行为.
     * 为了使得这一功能变得容易实现,await()方法的每一次调用都会返回barrier处到达线程的索引.
     * 然后你就可以选择用哪一个线程来执行barrier行为,举例如下:
     * if (barrier.await() == 0) {
     *   // log the completion of this iteration
     * }
     *
     * 在同步失败时,CyclicBarrier使用了一种all-or-none破损模型:如果一个线程因为中断(or执行过程的失败,超时等)过早的离开了barrier点,
     * 那么等待在barrier点的其他所有线程也会在同一时间因为BrokenBarrierException或者InterruptedException异常而离开barrier.
     *
     * 内存一致性影响:线程在调用await()方法之前的行为要优先于barrier action中的任何行为,barrier action成功返回这一行为要优先于
     * 所有其他等待线程await()后的行为.
     *
     *
     * @since 1.5
     * @see java.util.concurrent.CountDownLatch
     *
     * @author Doug Lea
     */
    public class CyclicBarrier {
        /**
         * barrier每一次使用都代表了一个generation实例.
         * 当barrier发生trip或者reset时,对应的generation会发生改变.
         * 由于非确定性,锁可能会分配给等待线程,因此可能会存在许多和使用barrier的线程相关的generation.
         * 但是每次只能激活这些线程中的一个(使用计数的那个),并且其他的线程要么broken要么trip.
         * 如果出现了一个暂停,但并未reset,则不需要一个激活的generation.
         */
        private static class Generation {
            boolean broken = false;
        }

        //barrier入口的保护锁
        private final ReentrantLock lock = new ReentrantLock();

        //trip前的等待条件
        private final Condition trip = lock.newCondition();

        //同时使用这个barrier的线程个数
        private final int parties;

        //当trip时,barrier定义的屏障操作.
        private final Runnable barrierCommand;

        //当前generation
        private Generation generation = new Generation();

        //等待线程数.每一个generation上,count值都会从parties降为0.
        //针对每一个新的generation或者当前barrier被broken时,count值都会被reset.
        private int count;

        //当barrier发生trip时,用于更新状态并唤醒每一个线程.
        //这一方法只在持有lock时被调用.
        private void nextGeneration() {
            // signal completion of last generation
            //标志最后一个generation完成.
            trip.signalAll();
            // set up next generation
            //为下一个generation赋初值
            count = parties;
            generation = new Generation();
        }

        //为当前barrier的generation类的唯一变量broken赋值,并唤醒所有线程.
        //这一方法只在持有lock时被调用.
        private void breakBarrier() {
            generation.broken = true;
            count = parties;
            trip.signalAll();//唤醒所有线程.
        }

        //此方法是barrier的主要代码,里面包括类很多实现上的策略.
        private int dowait(boolean timed, long nanos)
                throws InterruptedException, BrokenBarrierException,
                TimeoutException {
            final ReentrantLock lock = this.lock;//定义了一个final类型的可重入锁
            lock.lock();//加锁
            try {
                final Generation g = generation;//获取generation类

                //如果当前barrier已经broken了(其它线程出现问题导致barrier发生了broken)
                if (g.broken)
                    throw new BrokenBarrierException();//抛出broken异常

                //如果当前线程被中断
                if (Thread.interrupted()) {
                    breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
                    throw new InterruptedException();//抛出中断异常.
                }

                //index表示还需要几个线程调用await()方法,才能使得barrier发生trip
                int index = --count;
                if (index == 0) {  //barrier发生trip的条件满足[tripped]
                    boolean ranAction = false;
                    try {
                        final Runnable command = barrierCommand;//barrier初始化时定义的任务
                        if (command != null)
                            command.run();//当前线程调用:barrier定义的任务,说明barrier定义的任务由最后到达barrier的线程来完成.
                        ranAction = true;//barrier的任务执行完成
                        nextGeneration();//唤醒所有线程;重置count和generation值.
                        return 0;
                    } finally {
                        if (!ranAction)//如果barrier任务执行失败,则generation属性broken设为true;重置count值;唤醒所有线程.
                            breakBarrier();
                    }
                }

                //一直自旋直到发生:tripped, broken, interrupted, timed out
                for (;;) {
                    try {
                        if (!timed)//如果线程没有设置等待时间,则一直等待,直到其它线程将其唤醒
                            trip.await();
                        else if (nanos > 0L)//如果设置了超时时间,则等待制定时间.
                            nanos = trip.awaitNanos(nanos);
                    } catch (InterruptedException ie) { //自旋时被中断
                        if (g == generation && ! g.broken) { //如果其他线程还未出现问题导致当前barrier出现broken
                            breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
                            throw ie;
                        } else { //如果其他线程broken了当前barrier
                            //即使并未被中断,我们也会完成等待,因此这一中断被认作是"属于"后续的执行内容.
                            Thread.currentThread().interrupt();
                        }
                    }

                    //如果barrier被broken了,则抛出异常
                    if (g.broken)
                        throw new BrokenBarrierException();

                    //如果generation被重置,返回barrier在新一次的计数过程中,在可以trip之前还需要多少线程需要执行await()方法.
                    if (g != generation)
                        return index;

                    //如果线程设置了等待时间,且等时间<=0,抛出超时异常.
                    if (timed && nanos <= 0L) {
                        breakBarrier();//设置generation状态为broken;重置count值为parties;唤醒所有线程.
                        throw new TimeoutException();
                    }
                }
            } finally {
                lock.unlock();//解锁
            }
        }

        /**
         * 创建一个CyclicBarrier实例,参数parties指定了barrier上面的等待线程数.
         * 当barrier发生trip时,由最后一个进入barrier的线程完成指定的barrier action.
         *
         * @param parties 发生trip之前,必须调用await()方法的线程数目.
         * @param barrierAction 当barrier发生trip时,要执行的行为.如果没有需要执行的操作,此参数可以为null.
         */
        public CyclicBarrier(int parties, Runnable barrierAction) {
            if (parties <= 0) throw new IllegalArgumentException();
            this.parties = parties;
            this.count = parties;
            this.barrierCommand = barrierAction;
        }

        //不解释
        public CyclicBarrier(int parties) {
            this(parties, null);
        }

        //返回barrier需要发生trip的线程数目.
        public int getParties() {
            return parties;
        }

        /**
         * 线程持续等待直到此barrier上的所有线程都调用了await()方法.
         *
         * 如果当前线程并不是到达的最后一个线程,则它被禁用线程调度目的,并且处于休眠状态,直到发生以下事件之一:
         * 1.最后一个线程到达;
         * 2.其他线程中断了当前线程.
         * 3.其它线程中断了其它等待的线程.
         * 4.在barrier上面等待的线程发生超时.
         * 5.其它线程调用了barrier上面的reset方法.
         *
         * 如果当前线程:
         * 1.在进入这一方法时,中断状态位被标记.
         * 2.在等待过程中被中断
         * 则会抛出中断异常InterruptedException,且当前线程的中断状态被清除.
         *
         * 会抛出BrokenBarrierException异常的情况有:
         * 1.当其它线程在等待时,如果barrier被reset;
         * 2.当调用await()方法时barrier发生了broken
         *
         * 任意等待线程发生了中断异常时,其它等待线程都会抛出BrokenBarrierException,且barrier的状态会变为broken.
         *
         * 如果当前线程是最后一个到达barrier的线程,且构造函数中的barrier action非null,则在其它线程可以继续执行前,当前线程会执行
         * barrier action.
         * 如果在barrier action的执行过程中发生了异常,则该异常会对当前线程产生影响,且barrier的会处于broken状态.
         *
         * @return 当前线程到达索引,第一个到达的索引值为:getParties() - 1;
         *         最后一个到达的索引值为:0
         */
        public int await() throws InterruptedException, BrokenBarrierException {
            try {
                return dowait(false, 0L);
            } catch (TimeoutException toe) {
                throw new Error(toe); // cannot happen
            }
        }

        /**
         * 调用了dowait()方法,故细节可以到dowait()中查找.
         * @param timeout 在barrier处等待的时间
         * @param unit timeout参数的时间单位
         */
        public int await(long timeout, TimeUnit unit)
                throws InterruptedException,
                BrokenBarrierException,
                TimeoutException {
            return dowait(true, unit.toNanos(timeout));
        }


        //查询当前barrier是否处于broken状态.
        public boolean isBroken() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return generation.broken;
            } finally {
                lock.unlock();
            }
        }

        /**
         * 将barrier状态重置.如果此时有线程在barrier处等待,它们会抛出BrokenBarrierException并返回.
         * 注意:请注意,由于其他原因发生broken后重置可能会很复杂;线程需要通过一些方式来 完成同步,并选择一种方式完成reset.
         * 相对为后续的使用重建一个barrier,此重置操作更受欢迎.
         * 注意:这是一个需要加锁的操作.
         */
        public void reset() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                breakBarrier();   // break the current generation
                nextGeneration(); // start a new generation
            } finally {
                lock.unlock();
            }
        }

        //返回barrier处等待的线程数.这一方法在debug和assert操作中很有用.
        public int getNumberWaiting() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return parties - count;
            } finally {
                lock.unlock();
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/caoxiaohong1005/article/details/80000062