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

来源:互联网 发布:php下载系统源码 编辑:程序博客网 时间:2024/05/24 00:26

读前必看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,每跨越一次屏障点就换一个新的generationprivate 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,并"更新换代"。





原创粉丝点击