深入理解java并发——CyclicBarrier

CyclicBarrier有什么作用?
假如我们有一个20个人的会议,需要等所有人到齐之后,领导才能致开场词,这个场景抽象出来就可以使用CyclicBarrier。20个人相当于20个子线程,领导致辞相当于另一个主线程,每当一个子线程启动,计数加1,最后计数到20时表示人全部到齐,可以执行主线程,领导开始致辞!@#¥%……&……………. 好了 ,领导致辞完毕,20个人一起开始噼里啪啦鼓掌。。。。。。


以下为对CyclicBarrier的分析

变量:
/** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();//使用lock控制线程安全
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();//condition用来阻塞或者唤醒线程
    /** The number of parties */
    private final int parties;//需要线程个数
    /* The command to run when tripped */
    private final Runnable barrierCommand;//最后满足条件需要执行的主线程操作(可以不需要)
    /** The current generation */
    private Generation generation = new Generation();//Generation表示分代,每一组parties表示一代
    private int count;//计数时使用

构造方法:
一共有两个构造方法,其区别在于Runnable barrierAction,就是一个可执行的线程类,用于一些复杂的业务,比如,可以代表领导讲话......
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and
     * does not perform a predefined action when the barrier is tripped.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

核心方法await():

两个await方法,可以选择等待一定的时间
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

草鸡重要的方法await实际执行的方法: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();
        }
    }
该方法其大概执行流程是:如果等待中的线程有执行中断,或者对barrier执行reset操作,那么将会抛出相应的异常。。。。。。

其实await()的处理逻辑还是比较简单的:如果该线程不是到达的最后一个线程,则他会一直处于等待状态,除非发生以下情况:
最后一个线程到达,即index == 0
超出了指定时间(超时等待)
其他的某个线程中断当前线程
其他的某个线程中断另一个等待的线程
其他的某个线程在等待barrier超时
其他的某个线程在此barrier调用reset()方法。reset()方法用于将屏障重置为初始状态。(引用大神的原文:http://cmsblogs.com/?p=2241)
不出异常情况下:
1.如果当前count==0,说明线程数量已经够了,可以唤醒大boss了,即执行command.run(),当然我们在构造函数中可以不传递command,也就是没有大boss。但是nextGeneration()方法一定会执行,该方法主要是唤醒被condition.await()的线程,然后重新创建一个generation,新的一代barrier就这样产生了。。。。。。
private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }
2.当前count!=0,很简单,执行当前线程阻塞操作,通过conditon.await()实现。

简单案例:

/**
 * cycliBarrier会等待线程数量达到某一个值的时候才会继续往下执行,不满足该值的话该线程会阻塞
 * 假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,此时就可以利用CyclicBarrier了
 */

public class CyclicBarrierLearn {
    public static void main(String[] args) {
        int N = 20;
        CyclicBarrier barrier = new CyclicBarrier(N, new Thread() {
            @Override
            public void run() {
                System.out.println("领导开始发言了,呱唧呱唧呱唧");
            }
        });

        for (int i = 0; i < N; i++) {
            new Employee(barrier).start();
        }

    }

    static class Employee extends Thread {
        private CyclicBarrier cyclicBarrier;

        public Employee(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println("员工" + Thread.currentThread().getName() + "签到完毕,等待领导讲话");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("领导讲完话了,"+Thread.currentThread().getName()+"鼓掌中...");
        }
    }
}

结果
员工Thread-1签到完毕,等待领导讲话
员工Thread-3签到完毕,等待领导讲话
员工Thread-5签到完毕,等待领导讲话
员工Thread-9签到完毕,等待领导讲话
员工Thread-7签到完毕,等待领导讲话
员工Thread-11签到完毕,等待领导讲话
员工Thread-4签到完毕,等待领导讲话
员工Thread-13签到完毕,等待领导讲话
员工Thread-15签到完毕,等待领导讲话
员工Thread-2签到完毕,等待领导讲话
员工Thread-19签到完毕,等待领导讲话
员工Thread-17签到完毕,等待领导讲话
员工Thread-20签到完毕,等待领导讲话
员工Thread-16签到完毕,等待领导讲话
员工Thread-12签到完毕,等待领导讲话
员工Thread-18签到完毕,等待领导讲话
员工Thread-8签到完毕,等待领导讲话
员工Thread-10签到完毕,等待领导讲话
员工Thread-14签到完毕,等待领导讲话
员工Thread-6签到完毕,等待领导讲话
领导开始发言了,呱唧呱唧呱唧
领导讲完话了,Thread-6鼓掌中...
领导讲完话了,Thread-1鼓掌中...
领导讲完话了,Thread-5鼓掌中...
领导讲完话了,Thread-3鼓掌中...
领导讲完话了,Thread-9鼓掌中...
领导讲完话了,Thread-7鼓掌中...
领导讲完话了,Thread-11鼓掌中...
领导讲完话了,Thread-4鼓掌中...
领导讲完话了,Thread-13鼓掌中...
领导讲完话了,Thread-15鼓掌中...
领导讲完话了,Thread-2鼓掌中...
领导讲完话了,Thread-19鼓掌中...
领导讲完话了,Thread-17鼓掌中...
领导讲完话了,Thread-16鼓掌中...
领导讲完话了,Thread-20鼓掌中...
领导讲完话了,Thread-12鼓掌中...
领导讲完话了,Thread-18鼓掌中...
领导讲完话了,Thread-8鼓掌中...
领导讲完话了,Thread-10鼓掌中...
领导讲完话了,Thread-14鼓掌中...

总结:CyclicBarrier中的核心方法就是dowait()方法,使用lock+condition的方式保证线程安全,然后使用一个count计数器来记录是否达到条件。总结的不到位,希望大家批评指正。。。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值