Java并发之CyclicBarrier

来源:互联网 发布:windows cmd shell 编辑:程序博客网 时间:2024/05/16 00:25

    上一篇我们介绍了CountDownLatch的使用方法和实现原理,CountDownLatch的计数器只能使用一次,用来使一组线程阻塞于另一组线程。本篇我将介绍Java另一款并发工具CyclicBarrier。

一、同步屏障CyclicBarrier

    CyclicBarrier的字面意思是可循环使用的屏障。它的功能是,让一组线程到达一个屏障时被阻塞,知道最后一个线程到达屏障时,取消阻塞,所有被阻塞的线程才可以继续运行。

    对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过BrokenBarrierException(如果它们几乎同时被中断,则用InterruptedException)以反常的方式离开。

    CyclicBarrier函数列表

CyclicBarrier(int parties)创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。CyclicBarrier(int parties, Runnable barrierAction)创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。int await()在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。int await(long timeout, TimeUnit unit)在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。int getNumberWaiting()返回当前在屏障处等待的参与者数目。int getParties()返回要求启动此 barrier 的参与者数目。boolean isBroken()查询此屏障是否处于损坏状态。void reset()将屏障重置为其初始状态。


    CyclicBarrier使用简例:

public class Main {static CyclicBarrier cy = new CyclicBarrier(2,new Runnable(){@Overridepublic void run() {System.out.println("end barrier !"+Thread.currentThread().getName());}});public static void main(String[]args){Thread a = new Thread(new Runnable(){public void run() {try {System.out.println("a run");Thread.currentThread().sleep(1000);System.out.println("a meet barrier");cy.await();System.out.println("a end barrier");} catch (Exception e) {}}},"a");Thread b = new Thread(new Runnable(){public void run() {try {System.out.println("b run");Thread.currentThread().sleep(3000);System.out.println("b meet barrier");cy.await();System.out.println("b end barrier");} catch (Exception e) {}}},"b");Thread c = new Thread(new Runnable(){public void run() {try {System.out.println("c run");Thread.currentThread().sleep(3000);System.out.println("c meet barrier");cy.await();System.out.println("c end barrier");} catch (Exception e) {}}},"c");Thread d = new Thread(new Runnable(){public void run() {try {System.out.println("d run");Thread.currentThread().sleep(3000);System.out.println("d meet barrier");cy.await();System.out.println("d end barrier");} catch (Exception e) {}}},"d");a.start();b.start();try {Thread.currentThread().sleep(5000);} catch (InterruptedException e) {}cy.reset();c.start();d.start();}}/*output:a runb runa meet barrierb meet barrierend barrier !bb end barriera end barrierd runc rund meet barrierc meet barrierend barrier !cc end barrierd end barrier*/

二、CyclicBarrier实现原理

    CyclicBarrier结构类图

    由结构类图可知,CyclicBarrier中包含了一个ReentrantLock和一个Condition对象,它是通过独占锁实现的。

    构造函数

    CyclicBarrier共有两个构造函数,CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction)。源码如下:
    public CyclicBarrier(int parties, Runnable barrierAction) {        if (parties <= 0) throw new IllegalArgumentException();        this.parties = parties;//设置必须到达的线程数        this.count = parties;//还需要到达的线程的数量        this.barrierCommand = barrierAction;//当最后一个线程到达时,在解除屏障前,执行的方法    }

    等待函数

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

    等待函数是调用了dowait()方法实现的。

    private int dowait(boolean timed, long nanos)        throws InterruptedException, BrokenBarrierException,               TimeoutException {        final ReentrantLock lock = this.lock;//获取独占锁        lock.lock();//获取独占锁        try {            final Generation g = generation;//获取generation对象            if (g.broken)//如果锁已损坏,                throw new BrokenBarrierException();//抛出异常            if (Thread.interrupted()) {//如果当前线程被中断,则屏障被终止,唤醒所有线程                breakBarrier();                throw new InterruptedException();            }            int index = --count;//等待计数 -1            if (index == 0) {  // tripped//到达线程数足够                boolean ranAction = false;                try {                    final Runnable command = barrierCommand;//执行销毁前动作                    if (command != null)                        command.run();                    ranAction = true;                    nextGeneration();//唤醒所有等待线程 并更新generation                    return 0;                } finally {                    if (!ranAction)//抛出异常 执行动作失败 则同样终止屏障                        breakBarrier();                }            }            // loop until tripped, broken, interrupted, or timed out            for (;;) {//当前阻塞线程数不够                 try {                    if (!timed)//如果不是超时等待                        trip.await();//当前线程阻塞在 condition trip下                    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();//释放锁        }    }

    dowait的作用就是让当前线程阻塞,直到有parties个线程到达barrier、当前线程被中断、超时这三种情况时,当前线程才继续执行。

    1.generation是CyclicBarrier中的一个成员。

private Generation generation = new Generation();private static class Generation {    boolean broken = false;}


    同一批线程属于同一代,即同一个generation;CyclicBarrier通过内置generation对象,记录属于哪一代。每次屏障解除,都会换代。

    2.如果当前线程中断,则通过breakBarrier来终止CyclicBarrier

    private void breakBarrier() {        generation.broken = true;        count = parties;        trip.signalAll();    }

    它会调用condition的signalAll来唤醒所有线程。

    3.nextGeneration()方法会创建新的一代,并且唤醒所有上一代线程。

   private void nextGeneration() {        // signal completion of last generation        trip.signalAll();        // set up next generation        count = parties;        generation = new Generation();    }





原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 快手暂不支持音乐格式怎么办 上传视频不清晰怎么办 MP4格式嫌大怎么办 课堂派怎么办改成考试 手机信息幕变黑怎么办 手机百度太耗电怎么办 电脑打不开主页面怎么办? 贴吧被永久封了怎么办 晒课视频太大怎么办 有道精品课过期怎么办 手机缓存变小了怎么办 优学院课程过期怎么办 上公开课紧张怎么办 光纤被老鼠咬断怎么办 石灰粉进入眼睛怎么办 幼儿误吃粉笔怎么办? 吃了粉笔应该怎么办 小孩吃了颜料怎么办 小宝宝吃了纸怎么办 孩子不认真听讲怎么办 监控手机软件离线状态怎么办 云课堂忘记密码怎么办 广州办培训机构怎么办 一师一优课账号忘了怎么办 云相册空间不足怎么办 三星云空间不足怎么办 三星储存空间不足怎么办 宁阳县教育局强制补课怎么办 沉迷网络该怎么办英语 29岁沉迷游戏怎么办 学乐云登录不上怎么办 魔方学院无法识别怎么办 路由器卫士忘记密码怎么办 邮箱号忘记密码怎么办 水卡没钱了怎么办 旅行青蛙换手机怎么办 软件尚未受信任怎么办 百度搜不到的怎么办 百度中搜不到怎么办 山寨云网络异常怎么办 手机太重了怎么办