CyclicBarrier源码分析

CyclicBarrier是一个线程同步工具,用于让一组线程等待其他线程到达某个点后一起继续执行。它不同于CountDownLatch,CyclicBarrier是循环的,所有线程到达栅栏后一起执行任务。文章通过代码示例展示了CyclicBarrier的工作原理,并分析了其构造方法和内部机制,包括await方法的阻塞和唤醒过程。
摘要由CSDN通过智能技术生成

CyclicBarrier按照字面意思是环绕栅栏,它的作用的让所有的线程集合到一起,然后一起执行。它与CountDownLatch的区别是:CountDownLatch是一个线程等待其他线程完成后它才进入运行状态;CyclicBarrier是线程之间的相互等待,只有所有的线程都到达栅栏后才一起执行。如下图,线程1、线程2、线程3在栅栏前等待全部线程到齐才运行:

在这里插入图片描述

例子

public class CyclicBarrierDemo {

    static class Soldier implements Runnable {
        CyclicBarrier cyclicBarrier;
        int index;
        Soldier (CyclicBarrier cyclicBarrier,Integer index){
            this.cyclicBarrier = cyclicBarrier;
            this.index = index;
        }

        @Override
        public void run() {
            try {
                System.out.println("士兵"+index+"已经集合");
                //等待其他士兵集合
                cyclicBarrier.await();
                //全部士兵集合后,开始各种完成自己的任务
                Thread.sleep(Math.abs(new Random().nextInt() % 10000));
                System.out.println("士兵"+index+"已经完成了任务");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        int numbers = 5;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(numbers, new Runnable() {
            //栅栏放行前的任务
            @Override
            public void run() {
                System.out.println("全部士兵已经集合,开始执行任务");
            }
        });

        for(int i = 1; i <= numbers; i++){
            new Thread(new Soldier(cyclicBarrier,i)).start();
        }
    }

}

执行结果:可以看到执行结果,只有当所有的线程都到达栅栏后,栅栏才会放开,然后线程各自去获取锁执行任务。

士兵1已经集合
士兵3已经集合
士兵2已经集合
士兵4已经集合
士兵5已经集合
全部士兵已经集合,开始执行任务
士兵1已经完成了任务
士兵2已经完成了任务
士兵5已经完成了任务
士兵4已经完成了任务
士兵3已经完成了任务

源码分析

构造方法

CyclicBarrier是一个循环栅栏,parties规定栅栏新一轮期需要等待线程数量;barrierAction所有线程到达后,栅栏的回调方法;count表示当前轮期栅栏还需要等待多少线程才能放开。
CyclicBarrier是一个循环栅栏,每一轮执行完之后,count都会被重新初始化。

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

public CyclicBarrier(int parties) {
    this(parties, null);
}

成员变量

//独占锁,非公平模式
private final ReentrantLock lock = new ReentrantLock();

//条件,用来存储栅栏放开前阻塞的线程,当线程全部到达后,调用trip.signalAll()唤醒所有的线程
private final Condition trip = lock.newCondition();

//循环栅栏的初始等待线程数量
private final int parties;

//所有的线程到达后栅栏的回调方法,barrierCommand可以为空
private final Runnable barrierCommand;

//当前栅栏是否被中断,每一轮generation都会重新创建
private Generation generation = new Generation();

//循环栅栏当前需要等待多少线程才放行,count==0栅栏才放行
private int count;

await 阻塞线程

线程调用await方法来等待其他线程到齐,如果此时线程已经全部到齐,那么会调用ConditionsignalAll方法来唤醒阻塞线程。

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

await会调用dowait方法。dowait方法有两个参数:timed用来设置线程是否有等待超时机制,如果有,那么线程会阻塞nanos毫秒后超时。

private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    //1.获取独占锁         
    lock.lock();
    try {
      	//栅栏的当前轮次的状态
        final Generation g = generation;
		//2.判断栅栏是否被破坏,如果被破坏将抛出异常
        if (g.broken)
            throw new BrokenBarrierException();
		//判断线程是否被中断
        if (Thread.interrupted()) {
          	//如果线程被中断,将栅栏的状态设置为被破坏
            breakBarrier();
            throw new InterruptedException();
        }
      
		//index表示当前栅栏还需要等待多少线程到达
        int index = --count;
        //index =0 表示所有的线程已经到达栅栏
        if (index == 0) {  // tripped
          	//3.如果栅栏等待线程都已经到达
            boolean ranAction = false;
            try {
              	//执行栅栏的回调方法
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
              	//唤醒所有的线程
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

       
      	//4.栅栏等待线程没有全部到达
        for (;;) {
            try {
                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();
    }
}
  1. 获取独占锁。

    CyclicBarrier因为使用到了Condition,所以需要先调用ReentrantLocklock方法来获取锁。

  2. 检测栅栏是否被破坏

    CyclicBarrier支持线程中断,所以如果有线程被中断后,它除了自身抛出异常外,还会将栅栏的状态设置为被破坏,然后唤醒其他的线程。

    //线程被中断
    if (Thread.interrupted()) {
        breakBarrier();
        throw new InterruptedException();
    }
    
    private void breakBarrier() {
      			//设置栅栏状态
            generation.broken = true;
      			//重设count
            count = parties;
      			//唤醒线程,这些线程将被添加到独占锁的等待队列中去尝试获取锁
            trip.signalAll();
    }
    

    阻塞的线程被唤醒后,检测到栅栏被破坏,也将抛出异常。

    if (g.broken)
        throw new BrokenBarrierException();
    
  3. 线程全部到齐

    如果线程已经全部到齐后,在打开栅栏前执行回调任务。

     final Runnable command = barrierCommand;
     if (command != null)
        command.run();
     ranAction = true;
     //唤醒所有的线程
    nextGeneration();
    
    

    执行完barrierCommand任务后,将唤醒所有的阻塞线程,让线程各自去完成自己的任务。

    
    private void nextGeneration() {
        //唤醒所有的任务
        trip.signalAll();
        // set up next generation
        count = parties;
      	//重新设置Generation,新一轮的栅栏期
        generation = new Generation();
    }
    
    
  4. 线程没有全部到达

    如果线程没有到,那么将调用Conditionawait方法,将当前线程保存到Condition的阻塞队列,阻塞当前线程。Condition的源码分析参考Condition源码分析

if (!timed)
  	//一直阻塞,直到所有的线程都到达或者被中断
    trip.await();
else if (nanos > 0L)
  	//设置超时等待
    nanos = trip.awaitNanos(nanos);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值