• CyclicBarrier原理及源码解析


    概念

    通过AQS的互斥锁机制(ReentrantLock)+条件(Condition)实现的,用于一组线程相互之间等待,直到到达某个公共屏障点 (common barrier point),再继续执行。常用于多线程计算数据,当所有线程都完成执行后,在CyclicBarrier回调线程中合并计算。为方便源码解释,假定将一组线程表示为组

    public class CyclicBarrier {
        //Generation类在每组创建时都会生成一个该类的新的实例,用于下一组线程/reset方法后的复用,并恢复broken值
        private static class Generation {
            boolean broken = false; // 当前组有没有被强制中断
        }
        private final ReentrantLock lock = new ReentrantLock();
        //用于阻塞线程的条件变量,有未到组的线程,那么在该条件变量上等待,进入AQS条件队列
        private final Condition trip = lock.newCondition();
        //参与组的线程数
        private final int parties;
        //当所有的线程都参与到了组中后回调的方法
        private final Runnable barrierCommand;
        //代表当前组
        private Generation generation = new Generation();
        //还未到组的线程数
        private int count;
        
        public CyclicBarrier(int parties, Runnable barrierAction) {
            if (parties <= 0) throw new IllegalArgumentException();
            this.parties = parties;
            this.count = parties;
            this.barrierCommand = barrierAction;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    await方法

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe);
        }
    }
    
    private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException,
    TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;  //保存当前组的Generation快照,避免后面步骤的更新影响到这里的实例
            if (g.broken)
                throw new BrokenBarrierException();
            //组中有中断的线程,干掉组内其他线程并重新开始,此时并没有改变组中的Generation
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            int index = --count;
            if (index == 0) {  //最后一个到达组的线程,回调barrierCommand(若正常完成,那么不需要手动调用reset,因为这里调用了nextGeneration),然后唤醒所有阻塞在条件变量上的线程
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run(); //执行回调
                    ranAction = true;
                    nextGeneration(); // 进入下一个组
                    return 0;
                } finally {
                    // barrierCommand回调方法发生了异常,那么设置broken标志位
                    if (!ranAction)
                        breakBarrier();
                }
            }
            // 循环等待最后一个进入组的线程唤醒自己,或者被中断、或者等待超时。
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    reset方法

    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier(); // 将所有组内线程唤醒
            nextGeneration();  // 生成下一代
        } finally {
            lock.unlock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    breakBarrier方法

    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    nextGeneration方法

    private void nextGeneration() {
        trip.signalAll();
        count = parties;
        generation = new Generation(); // 生成了下一代组的实例
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

  • 相关阅读:
    bash shell 初体验-尚文网络xUP楠哥
    使用SpringBoot将图片上传至阿里云OSS
    SpringMVC简介
    【爬虫进阶】猿人学任务六之回溯(难度3.0)
    大模型LLM相关面试题整理
    全球农业经济论坛 丰收节贸促会-万祥军:产业链解决中国方案
    MD5加密
    【无标题】
    总结Java 并发编程 72 变
    Chromebook文件夹应用新功能
  • 原文地址:https://blog.csdn.net/zhang527294844/article/details/134011439