CyclicBarrier的使用、分析

CyclicBarrier的使用

一、含义:可循环屏障,意思可以多次使用,并且可以阻塞当前线程

二、使用场景:一般多线程下使用

三、使用方法:

	//只考虑正常情况下,不考虑线程中断、或者有异常产生...
	//1.实例化对象,parties为屏障放行的条件
	 CyclicBarrier barrier=new CyclicBarrier(parties);
	//2.让当前线程阻塞,每一次await,parties就会减一,直到为0时,就会放行
	//3.放行之后,会将parties恢复原来的值,也就是可以再次使用
	barrier.await();

四、一般用途

	//1.可以用来等待所有线程初始化后,再一起执行
	for(int i=0;i<THREAD_NUM;i++){
            final int num=i;
            new Thread(()->{
                try {
                    barrier.await();
                    System.out.println(num+":大家到齐了,开始执行了");
                    ......
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
	//2.可以用来等待所有线程结束
	for(int i=0;i<THREAD_NUM;i++){
            final int num=i;
            new Thread(()->{
                try {
               		 ......
                    //等到0时,就放行,斌且重置parties
                    barrier.await();
                    System.out.println(num+":大家到齐了,可以关闭了");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

五、源码分析

	//1.构造方法,有俩个,最终都执行这个构造方法
	//parties放行条件,barrierAction含义是路障(可以终止下次的循环,指的是下次的await)
	 public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }
	
	//2.await方法,最终指向这个方法,timed超时标记,nanos为超时时间
	 private int dowait(boolean timed, long nanos)
	//正常放行,关键代码,每一个线程进入都会进行加锁,并且接下来会--count,count的初始值就是parties
	//最终index为0时就是正常放行,这里自然会有疑问,尽然加锁那么index怎么可能会为0(不考虑parties为1),所以自然要去寻找释放锁的位置!!!
	lock.lock();
	.....
	int index = --count;
	
	//3.Lock锁的释放,未设置超时,直接释放锁,进入等待池;反之,等待一段时间在释放锁,进入等待池,这样子其他线程就有机会进入,那么index就有可能为0了,这样就可以进行放行了
	if (!timed)
	    trip.await();
	else if (nanos > 0L)
	    nanos = trip.awaitNanos(nanos);	

	//4.放行,一般情况下释放都是执行nextGeneration()来释放,主要就是给全局的generation重新赋值
	if (index == 0) {  // tripped
		//用于finally里的判断
        boolean ranAction = false;
         try {
         	//路障,默认情况下是null,所以这个run不会执行
             final Runnable command = barrierCommand;
             if (command != null)
                 command.run();
             ranAction = true;
             //唤醒所有等待池的线程,并且重置count,以及generation(其他线程退出循环的其中一个条件)!!!
             nextGeneration();//可以点进去看下,大概意思就这样,只有三行
             return 0;
         } finally {
             if (!ranAction)
             	//唤醒所有等待池的线程,并且重置count,以及设置generation.broken=true就是中断标志(其他线程退出循环的其中一个条件)!!!
                 breakBarrier();//可以点进去看下,大概意思就这样,只有三行
         }
     }
	
	//其他线程如何知道是否可以放行(正常放行)
	//index==0的那个线程执行了nextGeneration(),将全局的generation变量重新引用了一个值,所以这里的判断会为true,那么就可以退出循环了
	//至此所有线程就通过这个方式退出,就可以执行其他操作了
    if (g != generation)
        return index;
    
	//5.到这里为止,一直强调的正常放行,接下来看下其他放行方法(线程中断、generation.broken...)
	//看下以下代码,breakBarrier()注意这个方法
	 if (Thread.interrupted()) {
       	 breakBarrier();//!!!
         throw new InterruptedException();
     }
     if (!ranAction)
         breakBarrier();//!!!
     if (g == generation && ! g.broken) {
          breakBarrier();//!!!
          throw ie;
     }
     if (timed && nanos <= 0L) {
         breakBarrier();
          throw new TimeoutException();
     }
    //很明显所有的信息都指向这个方法,这个方法的代码
    private void breakBarrier() {
    	//更改这个broken标记,关键就在这个,这样其他线程可以通过判断退出
        generation.broken = true;
        //重置count
        count = parties;
        //唤醒所有线程
        trip.signalAll();
    }
    //线程退出的另一个判断,g就是generation,这就是breakBarrier()方法退出循环的方式,重新标记一下即可
    if (g.broken)
         throw new BrokenBarrierException();

	//既然知道了broken这个标记,那么接下来就是如何触发breakBarrier()这个方法了
	//6.触发breakBarrier()方法
	//index==0,放行代码里,这个代码的finally里面有一个breakBarrier(),而只有ranAction ==false时,if判断才可以通过,ranAction 的初始值也刚好是false
	//但有个问题,就是里面有ranAction = true这个操作,并且barrierCommand默认为null,那么ranAction = true这个操作必然会被执行,这样的话finally里的方法就必然不会被执行
	//那么想要ranAction = true;不被执行,就只有从barrierCommand(路障)入手了,因为只有command.run();这个出现问题(抛出异常),导致后面ranAction = true;不执行,那么finally就可以执行breakBarrier()
	//barrierCommand其实就是构造方法的第二个参数,所以只要使用这个构造方法,使得在某些条件下抛出一个异常,那么ranAction = true;就不会执行了,那么breakBarrier()就会被执行了
	if (index == 0) {  // tripped
        boolean ranAction = false;
         try {
         	//默认为null
             final Runnable command = barrierCommand;
             if (command != null)
                 command.run();
             //问题!!!
             ranAction = true;
             nextGeneration();
             return 0;
         } finally {
         	//只有 ranAction = true;不被执行才可以;也就是command.run();要抛出异常来中断下面的操作
             if (!ranAction)
                 breakBarrier();
         }
     }
	
	//调用线程中断方法也可以,Thread.interrupt(),这个仅仅是让线程标记为中断,而不是直接就中断,所以标记完后,下次在进入时,if判断通过后,也可以执行breakBarrier();
	if (Thread.interrupted()) {
        breakBarrier();
        throw new InterruptedException();
    }

	//设置超时也可以执行breakBarrier();
	if (timed && nanos <= 0L) {
       breakBarrier();
        throw new TimeoutException();
    }

六、源码分析总结

	1.退出循环有正常退出、broken标记退出、超时退出、线程中断退出
	2.正常退出,index==0即可
	  broken标记退出,执行了breakBarrier()方法即可
	  超时退出,设置了超时即可
	  线程中断退出,设置了线程中断即可
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值