AQS共享模式之CyclicBarrier源码解析

1.CyclicBarrier简单介绍

1.1运行示意图

在这里插入图片描述

1.2简单测试

import java.util.Random;
import java.util.concurrent.*;


/**
 *  使用线程池启动5个任务,模拟王者荣耀英雄进入游戏的过程。当所有的线程全部加载完毕后才能
 *  进入游戏,首先创建一个CyclicBarrier,初始化state = 5,每个线程加载完毕后调用await()
 *  方法阻塞,当所有的线程全部加载完毕阻塞后,state = 0,这时将所有的线程都唤醒。
 */

public class CyclicBarrierTest {
    
    

    public static void main(String[] args) {
    
    

        String[] heros = new String[]{
    
    "安琪拉", "亚瑟", "马超", "张飞", "刘备"};
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        CyclicBarrier barrier = new CyclicBarrier(5);
		
        //开启5个线程
        for (int i = 0; i < 5; i++) {
    
    
            threadPool.execute(new Player(heros[i], barrier));
        }
        threadPool.shutdown();
    }


    static class Player implements Runnable {
    
    
        private String hero;
        private CyclicBarrier barrier;

        Player(String hero, CyclicBarrier barrier) {
    
    
            this.hero = hero;
            this.barrier = barrier;
        }

        @Override
        public void run() {
    
    
            try {
    
    
                //模拟等待时间
                TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                System.out.println(hero + ": 加载进度100%, 等待其他玩家加载完成中。。。");
                //加载完毕 await()阻塞,等待其他线程加载完毕
                barrier.await();
                System.out.println(hero + "发现所有英雄加载完成,开始战斗吧 !");
            } catch (InterruptedException | BrokenBarrierException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

/* 运行结果
刘备: 加载进度100%, 等待其他玩家加载完成中。。。
亚瑟: 加载进度100%, 等待其他玩家加载完成中。。。
安琪拉: 加载进度100%, 等待其他玩家加载完成中。。。
张飞: 加载进度100%, 等待其他玩家加载完成中。。。
马超: 加载进度100%, 等待其他玩家加载完成中。。。
马超发现所有英雄加载完成,开始战斗吧 !
刘备发现所有英雄加载完成,开始战斗吧 !
安琪拉发现所有英雄加载完成,开始战斗吧 !
亚瑟发现所有英雄加载完成,开始战斗吧 !
张飞发现所有英雄加载完成,开始战斗吧 !
*/

2.源码解析

2.1内部结构分析

public class CyclicBarrier {
    
      
    
    //表示 "代" 这个概念 
	private static class Generation {
    
    
        
        /*
         * 表示当前代是够被打破,如果代被打破,那么再来到这一代的线程,就会直接抛出 
         * BrokenException异常,且在这一代挂起的线程都会被唤醒,然后抛出异常BrokenException。
         *
         *  在await()方法时,正常情况下被阻塞的线程被唤醒后,如果跳出await()就会判断
         *  原代和新代是否是一个,因为最后一个达到的线程会将创建新代。
         */
        boolean broken = false;
    }
	
    //因为CyclicBarrier是依赖于Condition等待队列的,而Condition等待队列必须依赖lock
    private final ReentrantLock lock = new ReentrantLock();
	
    /*
     * 线程挂起实现使用的等待队列,条件:当前代所有线程到位(count = 0),这个等待队列的线程才会被唤醒
     */
    private final Condition trip = lock.newCondition();
	
    //barrier需要参与进来的线程数量。
    private final int parties;
	
    //当前代 最后一个到位的线程需要执行的事件
    private final Runnable barrierCommand;
	
    //barrier对象,当前代
    private Generation generation = new Generation();
	
    //当前代还有多少个线程未到位。 (初始值为parties)。
    private int count;
    
    /*
     *  构造器
     *  @param parties 表示需要参与的线程数量,每次屏障需要参与的线程数
     *  @param barrierAction 当前 "代" 最后一个到位的线程,需要执行的事件(可以为NULL)
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
    
    
        //parties <= 0 抛出异常
        if (parties <= 0) throw new IllegalArgumentException();
        
        //为内部属性赋值
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

2.2简单方法分析

    /*
     * 开启下一代,当这一代 所有线程到位后(假设barrierCommond不为空,还需要最后一个线程
     * 执行玩事件), 会调用nextGeneration()开启下一代。
     */
	private void nextGeneration() {
    
    
        //将在条件队列内挂起的线程 全部唤醒
        trip.signalAll();
        
        //重置count 
        count = parties;
        
       //开启下一代 使用一个新的generation对象 表示新的一代,新的一代和上一代没有任何关系
        generation = new Generation();
    }
	
	/*
	 * 打破barrier屏障,在屏障内部的线程 都会抛出异常
	 */
    private void breakBarrier() {
    
    
        /*
         * 将 "代"中的broken设置为true,表示这一代是被打破了的,再来到这一代的线程
         * 直接抛出异常
         */   
        generation.broken = true;
        //重置count
        count = parties;
        
        /*
         * 将等待队列中的线程全部唤醒,唤醒后的线程会检查当前代是否是被打破的,
         * 如果是被打破的话,接下来的逻辑和开启下一代唤醒的逻辑不一样。
         */
        trip.signalAll();
    }

2.3await()方法

  public int await() throws InterruptedException, BrokenBarrierException {
    
    
        try {
    
    
            //底层调用的是dowait()方法,这里分析一个不带超时时间的,dowait()
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
    
    
            throw new Error(toe); 
        }
    }

//                    ||
//                    ||
//                    ||
//                    \/

   /*
    *  @param timed 表示当前调用await()方法的线程是否指定了超时时长,
    *  @param nanos 表示线程等待超时时长,如果timed = false,那么nanos = 0.
    */
   private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
    
    
        //获取全局锁
        final ReentrantLock lock = this.lock;
                   
        //加锁                 
        lock.lock();
        try {
    
    
            //获取barrier当前的 "代"
            final Generation g = generation;
			
            //如果当前代已经被打破状态,则当前调用await()方法的线程,直接抛出Broken异常
            if (g.broken)
                throw new BrokenBarrierException();
			
            //如果当前线程的中断标志位为true,则打破当前代,然后当前线程抛出中断异常。
            if (Thread.interrupted()) {
    
    
                /*
                 * 此方法将代中的broken设置为true,并重置count,唤醒等待队列的所有节点
                 */
                breakBarrier();
                //抛出中断异常。
                throw new InterruptedException();
            }
			
            /*
             * 线程执行到这里,说明当前线程中断状态是正常的(false),并且当前
             * "代"的broken为false(未打破状态)
             */
            
            // 将count - 1 赋值给 index
            int index = --count;
            
            /*
             * 条件成立:表示当前线程是最后一个到达barrier的线程。
             */
            if (index == 0) {
    
      // tripped
                
                /*
                 * ranAction -> true 表示最后一个到达barrier的线程在执行
                 * 内部的barrierCommand任务时没有抛出异常,否则抛出了异常
                 */
                boolean ranAction = false;
                try {
    
    
                    final Runnable command = barrierCommand;
                    
                    //barrierCommand不为NULL,将其执行
                    if (command != null)
                        command.run();
                    
                    //设置标记位为true。
                    ranAction = true;
                    
                    /*
                     * 开启新一代
                     *  1.唤醒等待队列内的线程,被唤醒的线程会依次获取到锁(state),然后依次退出await方法
                     *  2.重置count
                     *  3.创建一个新的generation,表示新的一代
                     */
                    nextGeneration();
                    
                    //返回0,因为当前线程是此代最后一个到达的线程,所以index == 0
                    return 0;
                } finally {
    
    
                    //执行barrierCommand出现异常
                    if (!ranAction)
                        //打破屏障
                        breakBarrier();
                }
            }
			
            /*
             * 执行到这里,说明当前线程并不是最后一个到达barrier的线程,此时需要进入自旋。
             */
            
            
            //自旋 一直到条件满足或者当前代被打破、线程被中断、等待超时
            for (;;) {
    
    
                try {
    
    
                    //条件成立: 说明当前线程是不指定超时时间的
                    if (!timed)
                        
                    //当前线程会释放掉lock,然后进入等待队列的尾部,然后挂起等待被唤醒
                        trip.await();
                    
                    //响应超时时间
                    else if (nanos > 0L)
                        //调用awaitNanos方法。(带超时的阻塞)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
    
     
                     /*
                      * 抛出中断异常,会进来这里
                      *  什么时候会抛出中断异常呢?
                      *  Node节点在等待队列内收到中断信号,会抛出中断异常
                      */
                     
                    /*
                     * g == generation 表示当前代并没有变化
                     * !g.broken 当前代如果没有被打破,那么当前线程就去打破,并
                     * 且抛出异常
                     */ 
                    if (g == generation && ! g.broken) {
    
    
                        breakBarrier();
                        throw ie;
                    } else {
    
    
						/*
						 * 执行到else有几种情况?
						 *  1.代发生了变化,这个时候就不需要抛出中断异常了,因为代已经更
						 *   新了,这里唤醒后就走正常逻辑了,只不过设置下中断标记
						 *  2.代没有发生变化,但是代被打破了,此时也不用返回中断异常,
						 *   执行到下面的时候会抛出 brokenBarrier异常,也记录下中断标志位
						 */
                        Thread.currentThread().interrupt();
                    }
                }
				
                /*
                 * 唤醒后执行到这里? 有几种情况?
                 *  1.正常情况,当前barrier开启了新的一代
                 *  2.当前generation被打破,此时也会唤醒所有在trip上挂起的线程
                 *  3.当前线程在等待队列中超时,然后主动转移到同步队列,然后获取到锁
                 */
                
                
                // 表示当前代已经被打破,
                if (g.broken)
                    //线程唤醒后依次抛出BrokenBarrier异常
                    throw new BrokenBarrierException();
				
                //条件成立:说明当前线程挂起期间,最后一个线程到位了,然后触发了开启新一轮的逻辑,此时唤醒等待队列中的线程,
                //这是一次正常的线程被唤醒后退出的逻辑
                if (g != generation)
                    return index;
				
                //超时判断。
                if (timed && nanos <= 0L) {
    
    
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
    
    
            //解锁。
            lock.unlock();
        }
    }

2.4await()流程图

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_46312987/article/details/121772474