多线程之循环栅栏CyclicBarrier及原理

读前必看ReentrantLock——http://blog.csdn.net/qq_31957747/article/details/74929911

一、循环栅栏CyclicBarrier

CyclicBarrier它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。比如将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归0,然后接着凑齐下一批10个线程,这就是循环栅栏内在的含义。


CountDownLatch和CyclicBarrier的区别:

1、 CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
2、 CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用。


构造方法:

CyclicBarrier(int parties)
CyclicBarrier(int parties, Runnable barrierAction)
其中parties表示计数总数,也就是参与的线程总数。barrierAction表示当计数器一次计数完成后,系统会执行的动作。


主要方法:

int await():在所有参与线程都已经在此 barrier 上调用 await 方法之前,将一直等待。
int await(long timeout, TimeUnit unit):在所有参与线程都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
int getNumberWaiting():返回当前在屏障处等待的参与者数目。
int getParties():返回要求启动此 barrier 的参与线程数目。
boolean isBroken():查询此屏障是否处于损坏状态。
void reset():将屏障重置为其初始状态。


例子:

司令官让士兵集合完成任务的场景

public class CyclicBarrierDemo {
    public static class Soldier implements Runnable{
        private String soldierName;
        private final CyclicBarrier cyclic;
        Soldier(CyclicBarrier cyclic,String soldierName){
            this.cyclic = cyclic;
            this.soldierName = soldierName;
        }
        @Override
        public void run() {
            try{
                //等待其他士兵到齐
                cyclic.await();
                doWork();
                //等待所有士兵完成工作
                cyclic.await();
            }catch (InterruptedException e){
                e.printStackTrace();
            }catch (BrokenBarrierException e){
                e.printStackTrace();
            }
        }

        void doWork(){
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(soldierName+" :任务完成");
        }
    }

    public static class BarrierRun implements Runnable{
        boolean flag;
        int N;
        public BarrierRun(boolean flag,int N){
            this.flag = flag;
            this.N = N;
        }
        @Override
        public void run() {
            if(flag){
                System.out.println("司令:[士兵"+N+"个,任务完成!]");
            }else{
                System.out.println("司令:[士兵"+N+"个,集合完毕!]");
                flag = true;
            }
        }
    }

    public static void main(String[] args) {
        final int N = 10;
        Thread[] allSoldier = new Thread[N];
        boolean flag = false;
        CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(flag,N));
        //设置屏障点,主要是为了执行这个方法
        System.out.println("集合队伍!");
        for(int i = 0;i < N;i++){
            System.out.println("士兵"+i+" 报道!");
            allSoldier[i] =new Thread(new Soldier(cyclic,"士兵"+i));
            allSoldier[i].start();
        }
    }
}
运行结果:

可以看到这段代码10个士兵线程,先后到达了2次屏障点,每次到达公共屏障点之后执行了BarrierRun的run方法,先后输出了集合完毕和任务完成。重点在于每个士兵都相互等待,直到所有的士兵一起加入,才进行下一步


二、CyclicBarrier源码分析

与之前介绍的同步类不同,CyclicBarrier是用ReentrantLockCondition来实现的。
首先看他的成员变量:

private static class Generation {
        boolean broken = false;
    }


private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition();

//参与线程的数量
private final int parties;
//所有参与线程到达屏障点后执行的动作
private final Runnable barrierCommand;
//当前的generation,同一批线程属于同一个generation,每跨越一次屏障点就换一个新的generation
private Generation generation = new Generation();
//还在等待的线程个数(注意:这里的等待不是condition队列中的等待线程,而是还没执行await()方法的线程,得明白每个等待的含义)
private int count;

构造方法:

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


我们主要分析他的await()方法:

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen;
        }
}

await()方法调用了dowait()方法:

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

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

            //如果线程被中断,就通过breakBarrier()破坏掉当前的generation,并唤醒所有的在condition队列中等待的线程
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            //还在等待的线程数-1
           int index = --count;
           if (index == 0) {  //所有线程均调用了await(),即到达公共屏障点
               boolean ranAction = false; //用来判断到达屏障点之后执行的动作是否正常执行
               try {
                   final Runnable command = barrierCommand;
                   if (command != null)
                       command.run();//注意这里是run()而不是start(),也就是说是同步
                   ranAction = true;
                   nextGeneration();//一次屏障跨越,”更新换代”,即唤醒所有的condition队列中的线程,并把count置为parties,然后换一个新的generation
                   return 0;
               } finally {
                   if (!ranAction)
                       breakBarrier();
               }
           }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {     //自旋
                try {
                    if (!timed)
                        trip.await();//将当前线程加入到condition队列,释放lock锁资源,使得下一个线程能获得锁资源进入lock块
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    //等待过程线程被中断
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        Thread.currentThread().interrupt();
                    }
                }
                // 如果“当前generation已经损坏”,则抛出异常。
                if (g.broken)
                    throw new BrokenBarrierException();

                 // 如果“generation已经更新换代”,则返回index。
                if (g != generation)
                    return index;
                 //”定时等待”且时间已到,则终止cyclicBarrier,唤醒condition队列中所有的线程
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            //释放lock锁
            lock.unlock();
        }
}

再看上面代码提到的breakBarrier()和nextGeneration()方法:

private void breakBarrier() {
        generation.broken = true;  //设置当前generation被破坏
        count = parties;          //重置count
        trip.signalAll();          //唤醒condition队列中的所有等待线程
    }

private void nextGeneration() {
        trip.signalAll();     //唤醒condition队列中的所有等待线程
        count = parties;    //重置count
        generation = new Generation();   //”更新换代”
    }

总结:

CyclicBarrier的关键在于利用ReentrantLock和Condition,每个调用await()方法的线程都被加入到condition队列中进行等待,所有参与线程都调用了await()之后,执行设置的后续动作barrierCommand,再唤醒condition队列中的所有等待线程,重置count,并"更新换代"。





  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值