Java高并发同步组件之CyclicBarrier源码解析

今天介绍最后一种常用的同步组件:CyclicBarrier。这是一种回环屏障,也就是可以让一组线程到达某一状态后再同时执行。这里可以重置CyclicBarrier的状态可以让它被重新利用。这个阻塞点就称为屏障点。等所有线程到达屏障后会冲破屏障,一起向下执行。

我们先来看看CyclicBarrier的类图,如下所示:

从类图可以看出,CyclicBarrier内部有一个Generation类。其内部使用ReentrantLock来实现,所以其实还是可以看成是基于AQS来实现的。

1、构造方法

CyclicBarrier的构造方法有两个,分别是传递parties和一个Runnable对象。

最终,都是调用下面的构造方法,源码如下:

这里,将传递进去的参数赋值给内部的parties和count。因为CyclicBarrier是可以复用的,因此调用await方法将count减1,直到0。然后,再次重新将parties赋值给count,然后可以重新开始使用CyclicBarrier。下面看完await的源码就理解了。

2、await方法

调用await方法,当前线程会阻塞。直到count减到0或者其他线程调用了中断方法,抛出异常。来看一下源码:

从源码可以看出,await最终调用的是dowait方法,第一个参数传递为false,第二个参数为0。

此外,还有一个带超时时间的await方法,来看一下源码:

从源码看出,其也是调用dowait方法,此时第一个参数为true,第二个参数为超时时间。我们接下来重点分析dowait方法即可。下面来看看dowait源码:

3、dowait源码

这一段代码比较长,不过很好理解。首先进行加锁,然后判断该线程有没有被中断,如果中断,则抛出异常。如果没有上面情况,就正常执行,count减一。然后判断count是否减到0了,如果到0了则定义一个变量ranAction为false。然后看看构造函数传递进来的Runnable对象(也就是barrierCommand)是否为空。如果不为空就执行run方法,否则设置ranAction为true。然后调用nextGeneration方法。nextGeneration方法源码如下:

这个nextGeneration方法其实就是唤醒trip这个条件队列里面阻塞的线程,然后重新设置count为parties。这里的trip是基于ReentrantLock得到的Condition队列。

我们接着看上面的dowait源码。如果index不为0,也就是count没有减到0。此时会进入最后这个无限循环中,此时,我们await方法调用dowait时传递的参数就派上用场了。

如果第一个参数timed为false,也就是调用无参数的await方法。此时没有设置超时时间,则直接执行到trip的await进行阻塞挂起。直到count为0,才能唤醒这些阻塞的线程。

如果调用带参数的await方法设置超时时间,此时timed也就是true,那么无限循环中就会进入else if分支,调用trip超时的awaitNanos方法,如果超时也会被激活。

总 结

1、CyclicBarrier时一种可以重复使用的同步组件。其内部定义了parties和count,当count为0时,会重新将parties赋值给count进行下一轮同步操作。

2、CyclicBarrier里面使用的是ReentrantLock和Condition队列,这样来实现线程的阻塞和唤醒。

3、CyclicBarrier核心的方法就是await方法,还有可以设置超时时间的await方法,其底层都是调用dowait实现的。所以,重点是dowait方法需要理解。

小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:730379855 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

猜你喜欢

转载自blog.csdn.net/weixin_44157163/article/details/87802906