jdk-CyclicBarrier(一)

CyclicBarrier有点类似CountDownLatch,貌似都是个倒数计数器,这里暂且不去管异同点,先把CyclicBarrier分析完,应该就能知道不同了。


//标志是否当前代,当前代我是这么理解的,每一次对象的生成就属于一代(个人观点)
    private static class Generation {
        boolean broken = false;
    }

    /** The lock for guarding barrier entry */
    //借助的依然是ReentrantLock锁,记得ReentrantLock内默认获取锁的方式是非公平锁
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    //借助了Condition条件等待队列,还有一个是AQS内的SYS等待队列,两者需要区分,在实际使用中是结合起来的
    //Condition队列中的线程是因为调用了await方法,进入了等待队列中,这时需要一个条件过来它才能加入到SYS队列中去竞争锁(注意这边)
    //SYS队列里的线程呢是由于竞争锁失败而加入进去的
    private final Condition trip = lock.newCondition();
    /** The number of parties */
    //总共有parties个线程需要进来
    private final int parties;
    /* The command to run when tripped */
    //所有线程都进来之后执行的语句
    private final Runnable barrierCommand;
    /** The current generation */
    //标志着哪一代,对于所有需要进入这一代的线程它们共享broken这个值,可以说成是区分每一代的屏障(屏障这个词来源于网络)
    private Generation generation = new Generation();

//dowait是核心方法,会抛出3中异常,这3中异常都有各自的处理
    private int dowait(boolean timed, long nanos)
            throws InterruptedException, BrokenBarrierException,
            TimeoutException {
        //获取当前lock锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //当前代信息
            final Generation g = generation;

            if (g.broken)
                //如果当前代被破坏了,直接抛出异常
                //个人觉得对于某一代需要进入的线程来说的话,这一个判断应该是小概率事件
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                //当前进入的线程中断了,抛异常,也是小概率事件
                breakBarrier(); //不过对于中断的线程来说的话,会执行breakBarrier操作
                throw new InterruptedException();
            }
            //统计进入的线程数量
            int index = --count;
            if (index == 0) {  // tripped
                //全部进入时才能进入执行
                boolean ranAction = false;
                try {
                    //command是可以传进来的,当所有线程进入时,可以先执行
                    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) {
                    //如果在执行await时发生InterruptedException异常,则抛出
                    if (g == generation && ! g.broken) {
                        //如果是当前代并且没有被破坏的话,将当前代标志设置成fasle,唤醒其他线程,最终抛出
                        breakBarrier();
                        throw ie;
                    } else {
                        //其他情况将线程标志为中断
                        //这里来看看着两个判断:
                        //g != generation,这种什么时候会出现呢?原本属于上一代的线程,然后又生成了新的generation对象,
                        //导致之前读取的g已经和generation不一致了,这种情况不会发生在第一个进入的线程,只能是后续线程不一致
                        //g.broken 被破坏,这个也很好理解,线程之间读取的值不一定就是最新的,有可能第一个线程进入到这边时,还是未被破坏的
                        //但是在这边刚刚要判断时,另外的线程破坏掉了。
                        //注意这边,可以看见处理breakBarrier()只能是第一个发现不一致的线程,那么后续线程不需要再做操作,直接抛出异常,也就是下面的逻辑段
                        Thread.currentThread().interrupt();
                    }
                }
                //对于await中断的线程,此处判断出被破坏了,直接抛出异常,不做处理
                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    //对于代数不一致的,返回当前代内的数量
                    return index;

                if (timed && nanos <= 0L) {
                    //超时的情况下会唤醒其他线程
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        //注意此处有新的generation生成,这就不难理解线程等待时有可能会出现代数不一致状态,因为有可能这个线程属于第一代,正准备中断时出现异常了,但是此时
        //已经到了第二代了,这种情况应该小概率
        generation = new Generation();
    }


    private void breakBarrier() {
        //将当前代的标志置成true,标志被破坏了
        generation.broken = true;
        count = parties;
        //唤醒在Condition队列中的其他线程,进入SYS队列中,获取锁
        trip.signalAll();
    }

测试个程序,来看看效果,把之前几种线程实现方式顺便写一写。

public class TestCycleB2 {
    //注意此处设置的屏障是3个线程,如果只有2个进入的话,就会无限等待
    private static CyclicBarrier cb = new CyclicBarrier(3);

    public static void main(String[] args) {

        //1.
        MyThreadTest thread1 = new MyThreadTest("线程1",cb);
        MyThreadTest thread2 = new MyThreadTest("线程2",cb);
        MyThreadTest thread3 = new MyThreadTest("线程3",cb);

        thread1.start();
        thread2.start();
        thread3.start();

        //2.
        for(int i = 0 ; i < 3; i++){
            final MyClass my = new MyClass("t"+i,cb);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    my.run();
                }
            }).start();
        }
        
        //3.
        for(int i = 0 ; i < 3; i++){
            new Thread(new MyThreadTest2(new MyClass("t"+i,cb))).start();
        }

    }
}



class MyThreadTest extends Thread{

    private CyclicBarrier cb;

    public MyThreadTest(String name,CyclicBarrier cb){
        super(name);
        this.cb = cb;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+ "即将进入等待");
        try{
            Thread.sleep(2000);
            cb.await();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+ "继续执行");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

class MyThreadTest2 implements Runnable{

    private MyClass my;

    public MyThreadTest2(MyClass my){
        this.my = my;
    }

    @Override
    public void run() {
        my.run();
    }
}
class MyClass{

    private String name;
    private CyclicBarrier cb;

    public MyClass(String name,CyclicBarrier cb){
        this.name = name;
        this.cb=cb;
    }

    public void run(){
        System.out.println(Thread.currentThread().getName()+ "即将进入等待");
        try{
            Thread.sleep(2000);
            cb.await();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+ "继续执行");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

得到的某一种情况,下面我们基于这种情况来分析,运行过程,加深理解。

Thread-0即将进入等待
Thread-2即将进入等待
Thread-1即将进入等待
Thread-0继续执行
Thread-2继续执行
Thread-1继续执行

基于上面某一次运行过程,可能存在下图运行图:



那我们来看看t0在调用await时发生了什么。主要就是释放当前线程占有的锁,加入到Condition队列中等待唤醒,并唤醒SYS队列中的线程,加入到获取锁的竞争中,此例中SYS没有其余线程。。Conditon队列的具体内部结构,不在这里分析了,前面文章已经分析过。


当t0释放出锁之后,由t2获取到锁,执行的逻辑与上图一致,最终也是中断在LockSupport处,并且加入到Condition中。至此大家全部park掉了,都等待。。。。注意,此处线程t1并没加入到Condition中,因为它是最后一个线程,进来之后就直接唤醒其他线程了,即发送signal信号.


当CyclicBarrier的count到达0时,就意味着大家都进来了,ok,就会执行trip.signalAll();这个signal操作就是之前分析的,与wait对应的唤醒操作,也就是Condition中线程锁等待的东东。

t1线程进入后发现全部到齐,那么就会执行唤醒操作了。唤醒操作中将Condition中的线程移到AQS等待队列中,直至t1释放锁之后,AQS等待队列中的线程被唤醒,去争夺锁。


被唤醒的t0线程,会去执行acquireQueue流程,这个在之前AQS文章中一分析过,后面的t2线程也是一样。上图对于AQS队列里的详情没有画的太精细,大致知道即可。

至此应该都分析的差不多了。。。。想到什么以后再补充吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值