java并发-线程通讯之CyclicBarrier源码分析

255 阅读2分钟

CyclicBarrier描述

作用:
假如现在有三个任务,要等着3个任务都执行到否一阶段再一起向下执行,使用CyclicBarrier可以很好的解决

CyclicBarrier可以多次使用
可能你看了countDwonLatch的实现原理,感觉都是都是传递一个基数,应该都是递减,实现原理应该类似
其实,都是递减基数是对的,但是实现原理,差异很大
countDwonLatch 是 直接使用 AQS 实现的
CyclicBarrier 则是通过 lock锁+condition 实现的

大致实现逻辑:
以上面的例子来说:
1.新建一个 cyclicBarrier 传入参数 parties 值为 3 
2.构造函数中,会把这个parties 复制出一份 count, 用作递减的具体数值,这也是cyclicBarrier可以多次使用的原因.
3.当其中一个线程执行到了需要等待的地方,那么他就会去检查count是否为0,这里面使用了lock,所以存在了condition
4.假如检查count 结果不为0 那么就会把当前线程放入 condition 等待队列 中
5.总有一个线程 --count 之后会发现count 的值为 0 了,那么这个时候会主动唤醒 condition 中所有的等待线程

实例dome

public static void main(String[] args) {

    CyclicBarrier cyclicBarrier = new CyclicBarrier(3,() -> {
        System.out.println("先执行当前任务");
    }) ; 
    for(int a=0; a<3; a++) {

        new Thread(() ->{

            try {
                Thread.sleep((long) Math.random() * 2000);

                int randomInt = new Random().nextInt(500);
                System.out.println("hello"+randomInt);

                cyclicBarrier.await();

                System.out.println("world"+randomInt);

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } ).start();

    }
}

//执行结果

hello275
hello31
hello187
先执行当前任务
world187
world275
world31

源码分析

构造函数

//第一个参数含义是有几个线程
//第二个参数含义是当线程都到达执行的位置等待,冲破屏障的时候先执行这个 runnable
//然后就是一些赋值操作 , 注意下 this.count = parties;
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

await


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

//第一个参数表示是否会有超时
//第二个参数表示等待超时时间
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 都是异常 
        if (g.broken)
            throw new BrokenBarrierException();

        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
		// 对 count 做 -- 操作
        int index = --count;
        //假如 --count 之后 值 为0  表示当前所有的线程都执行了到了等待的地方
        if (index == 0) {  // tripped
        	//这就是一个错误标志,就是上面的第一个异常触发的开关
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                //假如创建 cyclicBarrier 的时候传入了 runnable,那么先执行这个任务
                if (command != null)
                    command.run();
                //正常执行 会把这个设置为 true,然后下面的 breakBarrier 就不会被触发
                ranAction = true;
                // 这里面就是 唤醒 屏障前的代码 里面也就是调用了lock.newCondition().signalAll
                //同时再把 count的值 设置为 parties
                //然后重新创建 Generation 表示新一轮的开始
                nextGeneration();
                return 0;
            } finally {
            	//假如 --count == 0了  但是 没有执行到 ranAction = true; 
                //那么就会触发breakBarrier方法
                if (!ranAction)
                	// 这个方法会把当前一轮的任务处理掉,但是紧接着下次 这个 cyclicBarrier 就不能使用了
                    //因为这个方法把Generation 的  broken 改为了 true, 然后上面的 抛异常的代码就会执行
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        //
        for (;;) {
            try {
            	//这个地方判断有没有设置超时操作
                //我们看的是没有设置的
                //假如有超时操作也是调用的 condition 的 await 中完成的 
                if (!timed)
                	// 把当前线程放入 condition 的中,结束
                    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();
    }
}