并发编程 —— CyclicBarrier 讲解

CyclicBarrier 介绍

CyclicBarrier 是用于阻塞固定数量的线程,数量达到时就继续这些线程的执行,并可以指定继续执行这些线程之前执行 Runnable, 这个 Runnable 是在到达临界数量的那个线程执行的。

例子如下:

private static CyclicBarrier caBarrier = new CyclicBarrier(3, new Runnable() {
	
	@Override
	public void run() {
		System.out.println("阻塞结束,继续执行");
	}
});

public static void main(String[] args) {
	new Thread(){
		public void run() {
			System.out.println("线程一开始");
			System.out.println("线程一等待执行");
			try {
				caBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			System.out.println("线程一继续执行");
			System.out.println("线程一结束");
		};
	}.start();
	new Thread(){
		public void run() {
			System.out.println("线程二开始");
			System.out.println("线程二等待执行");
			try {
				caBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			System.out.println("线程二继续执行");
			System.out.println("线程二结束");
		};
	}.start();
	new Thread(){
		public void run() {
			System.out.println("线程三开始");
			System.out.println("线程三等待执行");
			try {
				caBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			System.out.println("线程三继续执行");
			System.out.println("线程三结束");
		};
	}.start();

打印结果:

线程一开始
线程一等待执行
线程二开始
线程二等待执行
线程三开始
线程三等待执行
阻塞结束,继续执行
线程三继续执行
线程三结束
线程二继续执行
线程二结束
线程一继续执行
线程一结束

CyclicBarrier 内部是通过记录一个阻塞线程的数量,每次调用 await() 都会减 1, 判断当前阻塞数量是否为 0, 为 0 则放开阻塞,await() 方法内部使用了 Lock 加锁来保证线程安全。
且 CyclicBarrier 是可重用的,这一轮的线程数量限制完之后,会再开始新的一轮的限制,例如把上面 caBarrier 创建时构造函数第一个参数改成 1, 执行结果如下:

线程一开始
线程一等待执行
阻塞结束,继续执行
线程一继续执行
线程一结束
线程二开始
线程二等待执行
阻塞结束,继续执行
线程二继续执行
线程二结束
线程三开始
线程三等待执行
阻塞结束,继续执行
线程三继续执行
线程三结束

CyclicBarrier 源码分析

创建 CyclicBarrier 对象时会指定线程的数量,以及等待数量完成后执行的 Runnable 对象,构造方法如下:

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

CyclicBarrier 在调用 await() 方法时会执行 doWait(false, 0L)

private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    // 通过 ReentrantLock 加锁保证多个线程同时调用问题
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final Generation g = generation;
        // ...
        int index = --count;
        if (index == 0) {  // 当执行到最后一个线程的 wait 方法时会走到这里
            boolean ranAction = false;
            try {
                // barrierCommand 是创建对象时指定的,所以这里可以看出是在最后一个 wait 的线程执行的
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                // 唤醒所有等待的线程,通过 LockSupport.unpark()
                nextGeneration();
                return 0;
            } finally {
                // ...
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                if (!timed)
                    // 阻塞当前线程,通过调用 LockSupport.park()
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                // ...
            }
            // ...
        }
    } finally {
        lock.unlock();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值