CyclicBarrier使用及源码讲解

一、介绍

从名字上来看CyclicBarrier,就是代表循环屏障。CyclicBarrier一般被称为栅栏。

Barrier屏障:让一个或多个线程达到一个屏障点,会被阻塞。屏障点会有一个数值,当达到一个线程阻塞在屏障点时,就会对屏障点的数值进行-1操作,当屏障点数值减为0时,屏障就会打开,唤醒所有阻塞在屏障点的线程。在释放屏障点之后,可以先执行一个任务,再让所有阻塞被唤醒的线程继续之后后续任务。

Cyclic循环:所有线程被释放后,屏障点的数值可以再次被重置。

CyclicBarrier是一种同步机制,允许一组线程互相等待。现成的达到屏障点其实是基于await方法在屏障点阻塞。

CyclicBarrier并没有基于AQS实现,他是基于ReentrantLock锁的机制去实现了对屏障点–,以及线程挂起的操作。(CountDownLatch本身是基于AQS,对state进行release操作后,可以-1)

CyclicBarrier没来一个线程执行await,都会对屏障数值进行-1操作,每次-1后,立即查看数值是否为0,如果为0,直接唤醒所有的互相等待线程。

CyclicBarrier对比CountDownLatch区别

  • 底层实现不同。CyclicBarrier基于ReentrantLock做的。CountDownLatch直接基于AQS做的。
  • 应用场景不同。CountDownLatch的计数器只能使用一次。而CyclicBarrier在计数器达到0之后,可以重置计数器。CyclicBarrier可以实现相比CountDownLatch更复杂的业务,执行业务时出现了错误,可以重置CyclicBarrier计数器,再次执行一次。
  • CyclicBarrier还提供了很多其他的功能:
    • 可以获取到阻塞的现成有多少
    • 在线程互相等待时,如果有等待的线程中断,可以抛出异常,避免无限等待的问题。
  • CountDownLatch一般是让主线程等待,让子线程对计数器–。CyclicBarrier更多的让子线程也一起计数和等待,等待的线程达到数值后,再统一唤醒

CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再一次执行。

二、应用

出国旅游:
导游小姐姐需要等待所有乘客都到位后,发送护照,签证等等文件,再一起出发
比如Tom,Jack,Rose三个人组个团出门旅游

在构建CyclicBarrier可以指定barrierAction,可以选择性指定,如果指定了,那么会在barrier归0后,优先执行barrierAction任务,然后再去唤醒所有阻塞挂起的线程,并行去处理后续任务。

所有互相等待的线程,可以指定等待时间,并且在等待的过程中,如果有线程中断,所有互相的等待的线程都会被唤醒。

如果在等待期间,有线程中断了,唤醒所有线程后,CyclicBarrier无法继续使用。

如果线程中断后,需要继续使用当前的CyclicBarrier,需要调用reset方法,让CyclicBarrier重置。

如果CyclicBarrier的屏障数值到达0之后,他默认会重置屏障数值,CyclicBarrier在没有线程中断时,是可以重复使用的。

public static void main(String[] args) throws InterruptedException {
    CyclicBarrier barrier = new CyclicBarrier(3,() -> {
        System.out.println("等到各位大佬都到位之后,分发护照和签证等内容!");
    });

    new Thread(() -> {
        System.out.println("Tom到位!!!");
        try {
            barrier.await();
        } catch (Exception e) {
            System.out.println("悲剧,人没到齐!");
            return;
        }
        System.out.println("Tom出发!!!");
    }).start();
    Thread.sleep(100);
    new Thread(() -> {
        System.out.println("Jack到位!!!");
        try {
            barrier.await();
        } catch (Exception e) {
            System.out.println("悲剧,人没到齐!");
            return;
        }
        System.out.println("Jack出发!!!");
    }).start();
    Thread.sleep(100);
    new Thread(() -> {
        System.out.println("Rose到位!!!");
        try {
            barrier.await();
        } catch (Exception e) {
            System.out.println("悲剧,人没到齐!");
            return;
        }
        System.out.println("Rose出发!!!");
    }).start();
    /*
    tom到位,jack到位,rose到位
    导游发签证
    tom出发,jack出发,rose出发
     */

}

三、 源码分析

分成两块内容去查看,首先查看CyclicBarrier的一些核心属性,然后再查看CyclicBarrier的核心方法

3.1 CyclicBarrier的核心属性

public class CyclicBarrier {
   // 这个静态内部类是用来标记是否中断的
    private static class Generation {
        boolean broken = false;
    }
    
    /** CyclicBarrier是基于ReentrantLock实现的互斥操作,以及计数原子性操作 */
    private final ReentrantLock lock = new ReentrantLock();
    
    /** 基于当前的Condition实现线程的挂起和唤醒 */
    private final Condition trip = lock.newCondition();
    
    /** 记录有参构造传入的屏障数值,不会对这个数值做操作 */
    private final int parties;
    
    /** 当屏障数值达到0之后,优先执行当前任务  */
    private final Runnable barrierCommand;
    
    /** 初始化默认的Generation,用来标记线程中断情况 */
    private Generation generation = new Generation();
    
    /** 每来一个线程等待,就对count进行-- */
    private int count;
}

3.2 CyclicBarrier的有参构造

掌握构建CyclicBarrier之后,内部属性的情况

// 这个是CyclicBarrier的有参构造
// 在内部传入了parties,屏障点的数值
// 还传入了barrierAction,屏障点的数值达到0,优先执行barrierAction任务
public CyclicBarrier(int parties, Runnable barrierAction) {
    // 健壮性判
    if (parties <= 0) throw new IllegalArgumentException();
    // 当前类中的属性parties是保存屏障点数值的
    this.parties = parties;
    // 将parties赋值给属性count,每来一个线程,继续count做-1操作。
    this.count = parties;
    // 优先执行的任务
    this.barrierCommand = barrierAction;
}

3.3 CyclicBarrier中的await方法

在CyclicBarrier中,提供了2个await方法

  • 第一个是无参的方式,线程要死等,直屏障点数值为0,或者有线程中断
  • 第二个是有参方式,传入等待的时间,要么时间到位了,要不就是直屏障点数值为0,或者有线程中断

无论是哪种await方法,核心都在于内部调用的dowait方法

dowait方法主要包含了线程互相等待的逻辑,以及屏障点数值到达0之后的操作

// 包含了线程互相等到的逻辑,以及屏障点数值到达0后的操作
private int dowait(boolean timed, long nanos)throws 
            // 当前新编程中断,抛出这个异常
            InterruptedException, 
            // 其他线程中断,当前线程抛出这个异常
            BrokenBarrierException,
            // await时间到位,抛出这个异常
            TimeoutException {
    // 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 拿到Generation对象的引用
        final Generation g = generation;

        // 判断下线程中断了么?如果中断了,直接抛出异常
        if (g.broken)
            throw new BrokenBarrierException();

        // 当前线程中断了么?
        if (Thread.interrupted()) {
            // 做了三个实现,
            // 设置broken为true,将count重置,唤醒其他等待的线程
            breakBarrier();
            // 抛出异常
            throw new InterruptedException();
        }

        // 屏障点做--
        int index = --count;
        // 如果屏障点为0,打开屏障啦!!
        if (index == 0) {  
            // 标记
            boolean ranAction = false;
            try {
                // 拿到有参构造中传递的任务
                final Runnable command = barrierCommand;
                // 任务不为null,优先执行当前任务
                if (command != null)
                    command.run();
                // 上述任务执行没问题,标记位设置为true
                ranAction = true;
                // 执行nextGeneration
                // 唤醒所有线程,重置count,重置generation
                nextGeneration();
                return 0;
            } finally {
                // 如果优先执行的任务出了问题i,就直接抛出异常
                if (!ranAction)
                    breakBarrier();
            }
        }

        // 死循环
        for (;;) {
            try {
                //  如果调用await方法,死等
                if (!timed)
                    trip.await();
                // 如果调用await(time,unit),基于设置的nans时长决定await的时长
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                // 到这,说明线程被中断了
                // 查看generation有没有被重置。
                // 并且当前broken为false,需要做线程中断后的操作。
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    Thread.currentThread().interrupt();
                }
            }
            // 是否是中断唤醒,是就抛异常。
            if (g.broken)
                throw new BrokenBarrierException();
            // 说明被reset了,返回index的数值。或者任务完毕也会被重置
            if (g != generation)
                return index;
            // 指定了等待的时间内,没有等到所有线程都到位
            if (timed && nanos <= 0L) {
                // 中断任务
                breakBarrier();
                // 抛出异常
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}
  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喝汽水的猫^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值