zoukankan      html  css  js  c++  java
  • 【JDK源码分析】并发包同步工具CyclicBarrier

    前言

    CyclicBarrier它是什么?一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。类似于朋友之间联系要在中午聚个会,几个朋友全部到齐后才开始喝酒吃菜。

    它内部维护了一个计数值count,每当一个线程就绪时该计数值就减小一次,当count为0时表示所有线程就绪,此时唤醒所有线程。

    源码

    CyclicBarrier属性和构造器

    public class CyclicBarrier {
        // 互斥锁
        private final ReentrantLock lock = new ReentrantLock();
        // 条件等待
        private final Condition trip = lock.newCondition();
        // 参与者数目
        private final int parties;
        // 栅栏放开时的执行任务
        private final Runnable barrierCommand;
        // 用于表示栅栏是否放开或者重置
        private Generation generation = new Generation();
        // 在等待的参数者数目
        private int count;
    
        // 构造parties个参与者的栅栏
        public CyclicBarrier(int parties) {
            this(parties, null);
        }
        // 构造parties个参与者的栅栏,并且设置栅栏放开时要执行的任务
        public CyclicBarrier(int parties, Runnable barrierAction) {
            if (parties <= 0) throw new IllegalArgumentException();
            this.parties = parties;
            this.count = parties;
            this.barrierCommand = barrierAction;
        }   
    }    

    CyclicBarrier方法

    1. await方法

    在所有参与者都已经在此 栅栏上调用 await 方法之前或者超时,会一直等待

        public public int int await() await() throws InterruptedException, BrokenBarrierException {
            throws InterruptedException, BrokenBar try {
                return dowait(false, 0L);
            } catch (TimeoutException toe) {
                throw new Error(toe); // cannot happen
            }
        }
    
        // 设置超时
        public int await(long timeout, TimeUnit unit)
            throws InterruptedException,
                   BrokenBarrierException,
                   TimeoutException {
            return dowait(true, unit.toNanos(timeout));
        }   
    
        private int dowait(boolean timed, long nanos)
            throws InterruptedException, BrokenBarrierException,
                   TimeoutException {
            // 给栅栏对象锁赋值
            final ReentrantLock lock = this.lock;
            // 加锁
            lock.lock();
            try {
                final Generation g = generation;
                // 栅栏状态
                if (g.broken)
                    throw new BrokenBarrierException();
                // 线程中断
                if (Thread.interrupted()) {
                    // 将栅栏状态设置成损坏、重置剩余等待线程数以及唤醒等待的线程
                    breakBarrier();
                    throw new InterruptedException();
                }
                // 每调用一次await减少一次count数
                int index = --count;
                if (index == 0) {  // tripped
                    // 所有参与者已到达栅栏位置
                    // 初始化执行任务状态,可见构造器设置的执行任务是由最后一个到达栅栏位置的线程来执行的
                    boolean ranAction = false;
                    try {
                        final Runnable command = barrierCommand;
                        if (command != null)
                            command.run();
                        ranAction = true;
                        // 重置栅栏并唤醒所有线程
                        nextGeneration();
                        return 0;
                    } finally {
                        if (!ranAction)
                            // command执行出错
                            // 任务执行出错将栅栏状态设置成损坏、重置剩余等待线程数以及唤醒等待的线程
                            breakBarrier();
                    }
                }
    
                // loop until tripped, broken, interrupted, or timed out
                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 {
                            // 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 {
           // 正常情况下,当最后一条线程执行到这里时,通过unlock来唤醒同步队列里的线程
                lock.unlock();
            }
        }

    breakBarrier方法调用的signalAll,以及dowait方法中trip.await、trip.awaitNanos在本人这篇【JDK源码分析】通过源码深入分析AbstractQueuedSynchronizer博客有分析,此处不再赘述。

       // 将栅栏状态设置成损坏、重置剩余等待线程数以及唤醒等待的线程
        private void breakBarrier() {
            generation.broken = true;
            count = parties;
            // 唤醒所有线程
            trip.signalAll();
        }
        // 重置栅栏,下次还可以再使用
        private void nextGeneration() {
            // 唤醒所有线程
            trip.signalAll();
            count = parties;
            generation = new Generation();
        }

    2. reset方法

    将栅栏重置为其初始状态并开启一个新的栅栏

        public void reset() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                breakBarrier();   // break the current generation
                nextGeneration(); // start a new generation
            } finally {
                lock.unlock();
            }
        }

    CyclicBarrier的源码分析到此结束

    用法

    如下面,张锅和张嫂约起去火锅店吃火锅,二个人到齐后才叫老板上菜,下面上示例:

            final CyclicBarrier cyclicBarrier = new CyclicBarrier(2,
                    () -> System.out.println(Thread.currentThread().getName() + "喊:老板,我们人到齐了,把菜上起!"));
    
            new Thread(() ->{
    
                System.out.println("张锅在去火锅店的路上...");
                try {
                    Thread.sleep(System.currentTimeMillis() % 1000);
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "张锅").start();
    
            new Thread(() ->{
                System.out.println("张嫂在去火锅店的路上...");
                try {
                    Thread.sleep(System.currentTimeMillis() % 1000);
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "张嫂").start();

    打印结果

    张锅在去火锅店的路上...
    张嫂在去火锅店的路上...
    张锅喊:老板,我们人到齐了,把菜上起!

    总结

    当遇到多条线程需要相互等待时,使用CyclicBarrier工具是一个比较好的选择。

  • 相关阅读:
    C——联合体(共同体)总结
    JMX操作ActiveMQ(1)
    使用xml和java代码混合控制UI界面
    Hive Metastore ObjectStore PersistenceManager自动关闭bug解析
    (算法课大报告)大数据的查找与排序
    编程珠玑---读书笔记---使用后缀数组查找最长重复子串
    VMware vSphere服务器虚拟化实验十一高可用性之三Fault Tolerance
    签名应用例子
    fopen()惹的祸
    Bigcommerce: 给已完成购买的客户发送一封产品评论邮件,让客户直接进行产品评论
  • 原文地址:https://www.cnblogs.com/d-homme/p/9363485.html
Copyright © 2011-2022 走看看