AQS 定义了两种资源共享方式
Exclusive: 独占,只有一个线程能执行,如ReentrantLock
Share: 共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier不同的自定义的同步器争用共享资源的方式也不同。
倒计数器CountDownLatch
CountDownLatch是一个非常实用的多线程控制工具类, “CountDown”在英文中意为 “门闩 ”,也就是说把门锁起来,不让里面的线程跑出来。
我们可以想象一下火箭发射,只有万事俱备,都调试好,才能发射。CountDownLatch可以使最后的点火线程等待所有检查线程全部完工后再执行 。 我们用术语进一步描述:
CountDownLatch
主要提供的机制是当多个(具体数量等于初始化CountDownLatch时count
参数的值)线程都达到了预期状态 或完成预期工作 时才可以触发事件,其他线程可以等待这个事件来触发自己的后续工作。到达自己预期状态的线程会调用CountDownLatch
的 countDown
方法 ,等待的线程会调用CountDownLatch的 await方法
。 值得注意的是,CountDownLatch是可以唤醒多个等待的线程的 如果CountDownLatch
初始化的count值为1,那么这就退化为一个单一事件了,也就是由一个线程来通知其他线程,效果等同于对象的wait和notifyAll , count值大于1是常用的方式,目的是为了让多个线程到达各自的预期状态,变为一个事件进行通知,线程则继续自己的行为。
倒计数器CountDownLatch的结构
分析前我们先看一下它的结构图: dsdaasdasdsadasdasd 通过结构图我们发现:CountDownLatch类只提供了一个构造器,并且 CountDownLatch类有一个内部类Sync,Sync继承于AQS,并没有其他得复杂结构,我们接着往下看: CountDownLatch类只提供了一个构造器:构造器调用了CountDownLatch
中的内部类Sync的 setState(count)
方法,我们发现这个方法实现在AQS基础组件中,并且count值就是同步状态state的值,如图:
public CountDownLatch ( int count) {
if ( count < 0 ) throw new IllegalArgumentException ( "count < 0" ) ;
this . sync = new Sync ( count) ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡
Sync ( int count) {
setState ( count) ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡
protected final void setState ( int newState) {
state = newState;
}
倒计数器CountDownLatch的方法
public void await ( ) throws InterruptedException {
sync. acquireSharedInterruptibly ( 1 ) ;
}
public boolean await ( long timeout, TimeUnit unit)
throws InterruptedException {
return sync. tryAcquireSharedNanos ( 1 , unit. toNanos ( timeout) ) ;
}
public void countDown ( ) {
sync. releaseShared ( 1 ) ;
}
public long getCount ( ) {
return sync. getCount ( ) ;
}
public String toString ( ) {
return super . toString ( ) + "[Count = " + sync. getCount ( ) + "]" ;
}
倒计数器CountDownLatch的使用
package com. wwj. lockdemos;
import java. util. concurrent. CountDownLatch;
import java. util. concurrent. ExecutorService;
import java. util. concurrent. Executors;
public class CountDownLatchDemo implements Runnable {
static final CountDownLatch end = new CountDownLatch ( 5 ) ;
static final CountDownLatchDemo demo = new CountDownLatchDemo ( ) ;
@Override
public void run ( ) {
try {
System. out. println ( end. toString ( ) ) ;
System. out. println ( end. getCount ( ) ) ;
Thread. sleep ( ( int ) Math. random ( ) * 1000 ) ;
System. out. print ( "检查完成 " ) ;
end. countDown ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
public static void main ( String[ ] args) throws InterruptedException {
ExecutorService executorService = Executors. newFixedThreadPool ( 10 ) ;
for ( int i= 0 ; i< 5 ; i++ ) {
executorService. submit ( demo) ;
}
end. await ( ) ;
System. out. println ( "Fire" ) ;
executorService. shutdown ( ) ;
}
}
5
java. util. concurrent. CountDownLatch@bdcdf9e [ Count = 5 ]
5
java. util. concurrent. CountDownLatch@bdcdf9e [ Count = 5 ]
java. util. concurrent. CountDownLatch@bdcdf9e [ Count = 5 ]
5
5
检查完成 检查完成 检查完成 检查完成 java. util. concurrent. CountDownLatch@bdcdf9e [ Count = 1 ]
1
检查完成 Fire
程序分析:当生成一个CountDownLatch
实例,计数量为5,这表示CountDownLatch上的线程(点火线程)需要这等待这5个线程完成任务(检查线程)后才能继续执行。每执行完一次检查,就通过countDown()
方法来通知CountDownLatch
,让其倒计数器减1,其实就是同步状态state减1。如图: 通过结果我们发现:
当我们把CountDownLatch的值设置成6,最后结果不会有Fire,程序也不会停止运行,因为它一直等待第6个线程去执行; 通过getCount()
方法我们也可以看出程序真正实现了多线程同时处理临界区域,因为返回了四个Count==5; 接下来我们进一步分析其原理。
倒计时器CountDownLatch 实现原理
我们重点分析两个方法await()
和tryAcquireShared(int acquires)
方法(因为最核心的程序前面的文章已经分析过,不清楚的可以去看一下):
重点看await()
的源码:
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
public void await ( ) throws InterruptedException {
sync. acquireSharedInterruptibly ( 1 ) ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
public final void acquireSharedInterruptibly ( long arg) throws InterruptedException {
if ( Thread. interrupted ( ) )
throw new InterruptedException ( ) ;
if ( tryAcquireShared ( arg) < 0 )
doAcquireSharedInterruptibly ( arg) ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
protected int tryAcquireShared ( int acquires) { 这个方法可以去和Semaphore和ReentrantLock的分别对比🖤🖤
return ( getState ( ) == 0 ) ? 1 : - 1 ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
private void doAcquireSharedInterruptibly ( int arg)
throws InterruptedException {
final Node node = addWaiter ( Node. SHARED) ;
boolean failed = true ;
try {
for ( ; ; ) {
final Node p = node. predecessor ( ) ;
if ( p == head) {
int r = tryAcquireShared ( arg) ;
if ( r >= 0 ) {
setHeadAndPropagate ( node, r) ;
p. next = null;
failed = false ;
return ;
}
}
if ( shouldParkAfterFailedAcquire ( p, node) && parkAndCheckInterrupt ( ) )
throw new InterruptedException ( ) ;
}
} finally {
if ( failed)
cancelAcquire ( node) ;
}
}
acquireSharedInterruptibly()
的作用是获取共享锁:
如果当前线程是中断状态,则抛出异常InterruptedException
; 否则,调用tryAcquireShared(arg)尝试获取共享锁;
尝试成功则返回,否则就调用doAcquireSharedInterruptibly()
,该方法会使当前线程一直等待,直到当前线程获取到共享锁(或被中断)才返回。
再看cutdown()
方法源码:
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
public void countDown ( ) {
sync. releaseShared ( 1 ) ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
public final boolean releaseShared ( int arg) {
if ( tryReleaseShared ( arg) ) { 💛💛💛💛💛
signalNext ( head) ;
return true ;
}
return false ;
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
protected boolean tryReleaseShared ( int releases) {
for ( ; ; ) {
int c = getState ( ) ;
if ( c == 0 )
return false ;
int nextc = c - 1 ;
if ( compareAndSetState ( c, nextc) )
return nextc == 0 ;
}
}
🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡🧡🧡 🧡🧡
private static void signalNext ( Node h) {
Node s;
if ( h != null && ( s = h. next) != null && s. status != 0 ) {
s. getAndUnsetStatus ( WAITING) ;
LockSupport. unpark ( s. waiter) ;
}
}
用法总结 :
CountDownLatch
的countDown()
用法是当一个线程执行完工作后,通过countDown()
来使同步状态state-1,并且只有同步状态为0时才会通知同步队列中头节点的下一个结点,改变其状态然后unpark()
;通过await()
方法,只有同步状态为0时,才能加入到队列中,后续的操作就和Semaphore都一样了,通过await()方法我们发现它执行完,并不会重置同步状态state,那这时候就不能继续执行下去,也可以说CountDownLatch
是一次性的,那么有没有它的改善呢?当然,就是接下来要讲的循环栅栏CyclicBarrier
,期待! 总结:
CountDownLatch是通过“共享锁”实现的; 在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”的同步状态,即最多能被count个线程同时获取。 当某线程调用该CountDownLatch
对象的await()
方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行; 而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!
循环栅栏CyclicBarrier
CyclicBarrier
是另外一种多线程并发工控制工具。其实和CountDownLatch非常类似,它也可以实现线程间的计数等待 ,但是它比CountDownLatch更加复杂,但是如果了解ReentrantLock和Condition的话,其实比CountDownLatch更简单。CyclicBarrier
是一个同步辅助类,允许一组线程互相等待 ,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以也称它为循环的栅栏。CyclicBarrier
和CountDownLatch
的对比:
CountDownLatch 基于 AQS 的共享模式的使用,而 CyclicBarrier 基于 Condition 来实现的 。 CountDownLatch的作用:允许1或N个线程等待其他线程完成执行 ;而CyclicBarrier则是允许N个线程相互等待 ; CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用 ;
循环栅栏CyclicBarrier的结构
分析CyclicBarrier前我们先看一下它的结构图: 从结构图可以看出:它的逻辑结构很简单,CyclicBarrier
是包含了"ReentrantLock
对象lock"和"Condition
对象trip" ,它是通过独占锁实现的: CyclicBarrier类的内部有一个计数器(没有标注出来),每个线程在到达屏障点的时候都会调用await()
方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await()
方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待的原理,下面我们先看看CyclicBarrier有哪些成员变量:
private static class Generation {
Generation ( ) { }
boolean broken;
}
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;
我们再看一下CyclicBarrier中的所有成员变量:
trip: CyclicBarrier内部通过条件队列trip来对线程进行阻塞的;parties: 表示每次拦截的线程数 ,该值在初始化CyclicBarrier时进行赋值;
count: 内部计数器 ,它的初始值和parties相同,以后随着每次await()
方法的调用而减1,直到减为0就将所有线程唤醒,然后执行任务(也可不执行),换代等操作; Generation: CyclicBarrier中的静态内部类Generation,该类的对象代表栅栏的当前代,每次count0就更新换代,利用它可以实现循环等待 。barrierCommand: 表示换代前执行的任务,当count减为0时表示要换代了。在换代之前会将所有阻塞的线程唤醒,在唤醒所有线程之前你可以通过指定barrierCommand来执行自己的任务 。我们通过一张图来进一步了解(转载的图):
循环栅栏CyclicBarrier的函数列表
CyclicBarrier ( int parties)
CyclicBarrier ( int parties, Runnable barrierAction)
int await ( )
int await ( long timeout, TimeUnit unit)
int getNumberWaiting ( )
int getParties ( )
boolean isBroken ( )
void reset ( )
循环栅栏CyclicBarrier的使用
我们通过两个实际中的例子来进一步理解怎么使用这个CyclicBarrier
:
当司令下达命令,要求10个士兵一起去完成一项任务,这时需要他们先集合报道,然后去执行任务,当10个士兵把各自任务都执行完了,司令才对外宣布,任务完成! 吃饭时要等全家人都上座了才动筷子,旅游时要等全部人都到齐了才出发,比赛时要等运动员都上场后才开始。 接下来我们通过程序看到底如何简单使用CyclicBarrier:
package com. wwj. lockdemos;
import java. util. concurrent. BrokenBarrierException;
import java. util. concurrent. CyclicBarrier;
public class CyclicBarrierDemo {
private static final int size = 5 ;
private static CyclicBarrier cyclicBarrier;
static class InnerThread extends Thread {
@Override
public void run ( ) {
try {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "等待栅栏" ) ;
cyclicBarrier. await ( ) ;
System. out. println ( Thread. currentThread ( ) . getId ( ) + "continue" ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} catch ( BrokenBarrierException t) {
t. printStackTrace ( ) ;
}
}
}
public static void main ( String[ ] args) {
cyclicBarrier = new CyclicBarrier ( size) ;
for ( int i= 0 ; i< 5 ; i++ ) {
new InnerThread ( ) . start ( ) ;
}
}
}
结果:
Thread- 1 等待栅栏
Thread- 3 等待栅栏
Thread- 0 等待栅栏
Thread- 4 等待栅栏
Thread- 2 等待栅栏
17 continue
15 continue
18 continue
16 continue
19 continue
结果分析:
主线程中新建了5个线程,所有的这些线程都调用cyclicBarrier.await()
等待。所以这些线程一直等待,直到cyclicBarrier中所有线程都达到barrier时,这些线程才继续运行! 那如何突出循环二字呢?我们再看另一个程序,也就是我们开始举的第一个例子,士兵命令士兵完成任务。(先集合,全都执行完任务)这两次都需要循环栅栏的实现:
package com. wwj. lockdemos;
import java. util. concurrent. BrokenBarrierException;
import java. util. concurrent. CyclicBarrier;
public class CyclicBarrierDemo1 {
public static class Solider implements Runnable {
static int i= 0 ;
private CyclicBarrier barrier ;
private String name;
public Solider ( CyclicBarrier barrier , String name) {
this . barrier = barrier;
this . name = name;
}
@Override
public void run ( ) {
try {
barrier. await ( ) ;
DoWork ( ) ;
barrier. await ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} catch ( BrokenBarrierException t) {
t. printStackTrace ( ) ;
}
}
static void DoWork ( ) {
try {
System. out. print ( "士兵" + ++ i + ":任务完成" ) ;
Thread. sleep ( ( int ) Math. random ( ) * 1000 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
public static class BarrierRun implements Runnable {
private boolean flag;
private final int N;
public BarrierRun ( boolean flag , int N) {
this . flag = flag;
this . N = N;
}
@Override
public void run ( ) {
if ( flag) {
System. out. println ( ) ;
System. out. println ( "司令:[士兵" + N+ "个,任务完成!]" ) ;
} else {
System. out. println ( ) ;
System. out. println ( "司令:[士兵" + N+ "个,集合完毕!]" ) ;
flag = true ;
}
}
}
public static void main ( String[ ] args) {
final int N= 5 ;
boolean flag = false ;
CyclicBarrier cyclicBarrier = new CyclicBarrier ( 5 , new BarrierRun ( flag, N) ) ;
Thread[ ] threads = new Thread [ 5 ] ;
for ( int i= 0 ; i< 5 ; i++ ) {
System. out. print ( "士兵" + ( i+ 1 ) + ":集合完毕 " ) ;
threads[ i] = new Thread ( new Solider ( cyclicBarrier, "士兵" + i) ) ;
threads[ i] . start ( ) ;
}
}
}
分析:
我们可以看出集合完毕意味着CyclicBarrier的一次计数完成,当再一次调用CyclicBarrier.await()
方法的时候,会进行下一代的计数。这次计数在程序中对应的就是显示士兵是否都完成了任务; 程序中两个内部类都扩展了Runnable接口,一个是为当更新换代时先执行相应的操作,另一个是为了让线程执行,注意它们的区别; CyclicBarrier.await()
方法可能会抛出两个异常:
一个是InterruptedException
,也就是在等待过程中线程被中断; 另一个是BrokenBarrierException
异常,这个异常标识当前的CyclicBarrier
已经破损了,我们通过程序具体说明其解决的问题: 当我们把程序的一部分改成如下:
for ( int i= 0 ; i< 5 ; i++ ) {
System. out. print ( "士兵" + ( i+ 1 ) + ":集合完毕 " ) ;
threads[ i] = new Thread ( new Solider ( cyclicBarrier, "士兵" + i) ) ;
threads[ i] . start ( ) ;
if ( i== 2 ) {
threads[ 0 ] . interrupt ( ) ;
}
}
分析:为什么会这样呢?
这样做,我们很可能会抛出1个InterruptedException
,4个BrokenBarrierException
,InterruptedException
是被中断线程抛出的,而其他4个BrokenBarrierException
则是等待在当前CyclicBarrier上的线程抛出的。这个异常可以避免其他4个线程进行永久的、无谓的等待(因为其中一个中断了,没有结果)。 接下我们看其源码,进一步理解CyclicBarrier的使用。
循环栅栏CyclicBarrier的实现原理
public CyclicBarrier ( int parties) {
this ( parties, null) ;
}
public CyclicBarrier ( int parties, Runnable barrierAction) {
if ( parties <= 0 ) throw new IllegalArgumentException ( ) ;
this . parties = parties;
this . count = parties;
this . barrierCommand = barrierAction;
}
通过构造器我们发现:
CyclicBarrier
初始化时本质调用的是构造器2,当更新换代时要先执行barrierAction
中的任务,然后才能换代,同时将计数器count
的值重新设置为parties; 接下来我们再看CyclicBarrier
中最重要的功能—>等待await():
public int await ( ) throws InterruptedException, BrokenBarrierException {
try {
return dowait ( false , 0 L) ;
} catch ( TimeoutException toe) {
throw new Error ( toe) ;
}
}
public int await ( long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException {
return dowait ( true , unit. toNanos ( timeout) ) ;
}
CyclicBarrier
类最主要的功能就是使先到达屏障点的线程阻塞并等待后面的线程,其中它提供了两种等待的方法,分别是定时等待和非定时等待。通过这两个方法,我们可以看到不管是定时等待还是非定时等待,它们都调用了dowait()
方法,只不过是传入的参数不同而已。 下面我们就来看看dowait()
方法;
重点看dowait()
方法的源码
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 ( ) ;
}
int index = -- count;
if ( index == 0 ) {
Runnable command = barrierCommand;
if ( command != null) {
try {
command. run ( ) ;
} catch ( Throwable ex) {
breakBarrier ( ) ;
throw ex;
}
}
nextGeneration ( ) ;
return 0 ;
}
for ( ; ; ) {
try {
if ( ! timed)
trip. await ( ) ;
else if ( nanos > 0 L)
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 <= 0 L) {
breakBarrier ( ) ;
throw new TimeoutException ( ) ;
}
}
} finally {
lock. unlock ( ) ;
}
}
上述代码大部分逻辑上都很简单,只不过是异常的处理部分我们需要注意细节,我们重新理一下dowait()
这个方法:
每次都将count减1
,减完后立马进行判断看看是否等于0:
如果等于0的话就会先去执行之前指定好的任务,执行完之后再调用nextGeneration
方法将栅栏转到下一代:
在nextGeneration
方法中会将所有线程唤醒,将计数器的值重新设为parties,最后更新换代。 在执行完nextGeneration
方法之后就意味着已经换代成功。 如果计数器此时还不等于0的话就进入for循环,根据参数来决定是调用trip.awaitNanos(nanos)
还是trip.await()
方法,这两方法对应着定时和非定时等待; 如果在等待过程中当前线程被中断就会执行breakBarrier
方法,该方法叫做打破栅栏,设置generation
的broken
状态为true
并唤醒所有线程。同时这也说明在等待过程中有一个线程被中断则这个“时代”就结束了 ,所有之前被阻塞的线程都会被唤醒。 线程醒来后会执行下面三个判断:
看看是否因为调用breakBarrier
方法而被唤醒,如果是则抛出异常; 看看是否是正常的换代操作而被唤醒,如果是则返回计数器的值; 看看是否因为超时而被唤醒,如果是的话就调用breakBarrier
打破栅栏并抛出异常; 这里还需要注意的是,如果其中有一个线程因为等待超时而退出,其他线程都会被唤醒。
再看dowait()中一些其他方法的源码
private void breakBarrier ( ) {
generation. broken = true ;
count = parties;
trip. signalAll ( ) ;
}
private void nextGeneration ( ) {
trip. signalAll ( ) ;
count = parties;
generation = new Generation ( ) ;
}
我们发现这两个方法其实区别并不大:
相同点:都重置了计数器count,然后唤醒所有的线程 不同点:breakBarrier()
将当代的栅栏状态设置为已破坏,nextGeneration()
顾名思义创建了新的一代generation
再看一个方法:
public void reset ( ) {
final ReentrantLock lock = this . lock;
lock. lock ( ) ;
try {
breakBarrier ( ) ;
nextGeneration ( ) ;
} finally {
lock. unlock ( ) ;
}
}
通过reset()源码我们可以发现:
如果初始化时,指定了线程 parties = 4,前面有 3 个线程调用了 await 等待,在第 4 个线程调用 await 之前,我们调用 reset 方法,那么会发生什么? 首先,打破栅栏,那意味着所有等待的线程(3个等待的线程)会唤醒,await()
方法会通过抛出 BrokenBarrierException
异常返回。然后开启新的一代,重置了 count
和 generation
,相当于一切归零了。 到此,循环栅栏CyclicBarrier就介绍完了。 文章部分内容参考如下点击!!!!!