一、CyclicBarrier类
CyclicBarrier类可以实现一组线程相互等待,当所有的线程达到某个屏障点以后再做后续的操作;
CyclicBarrier类是可重复的栅栏;
二、CyclicBarrier类和CountDownLatch类的区别
CyclicBarrier强调的是N个线程相互等待,只要一个线程的任务没有完成,所有的线程均在等待状态;
CountDownLatch类则是一个线程等待多个线程完成某件事情;
CyclicBarrier类则是多个线程相互等待,大家都完成以后再继续后面的事情;
CountDownLatch类是减计数,使用时一次性的;
CyclicBarrier类是加计数,可以重复使用;
三、CyclicBarrier类的实现原理
在CyclicBarrier的内部定义了一个Lock对象:
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
每当一个线程调用await()方法时,将拦截器线程数减1,然后判断剩余拦截数是否为初始值parties:
如果不是,则进入Lock对象的等待队列;
如果是,执行BarrierAction对象的Runnable方法;
然后将锁的条件队列中的左右的线程放在lock等待队列中;
这些线程会依次获取锁和释放锁;
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
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()) {
//将中断的线程设置为true,并同时其他阻塞的线程这个线程中断
breakBarrier();
throw new InterruptedException();
}
//获取线程的下标
int index = --count;
//如果下标为0,说明最后一个线程调用了这个方法
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//执行任务
if (command != null)
command.run();
//执行完成
ranAction = true;
//更新新的一代,重置count,唤醒新的线程
nextGeneration();
return 0;
} finally {
//如果执行出错,将 损坏的状态设置为true
if (!ranAction)
//将中断的线程设置为true,并同时其他阻塞的线程这个线程中断
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) {
//将中断的线程设置为true,并同时其他阻塞的线程这个线程中断
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();
}
}
//如果任何线程中断了,就会调用breakBarrier
//此时就会唤醒起他线程,其他线程被唤醒时候也会抛出BrokenBarrierException异常
//表示线程又中断
if (g.broken)
throw new BrokenBarrierException();
//如果正常换代了,那么就会返回当前线程的下标
if (g != generation)
return index;
//如果超时或者时间小等于0了,那么就会报错TimeoutException,超时
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放锁
lock.unlock();
}
}
四、CyclicBarrier类的构造方法
//创建一个新的CyclicBarrier,当给定线程为parties数量的线程等待时,在没有到parties数量时,将不会执行预定义动作
CyclicBarrier(int parties)
//创建一个新的CyclicBarrier,当给定线程为parties数量的线程等待时,在没有到parties数量时,将不会执行预定义动作;
//当到达parties数量时,执行barrierAction线程
CyclicBarrier(int parties, Runnable barrierAction)
五、栗子:3人一组吃饭,然后拍照留念。
public static void main(String[] args) {
//3个人聚餐
final CyclicBarrier cb = new CyclicBarrier(3, () -> {
System.out.println("人员全部到齐了,拍照留念。。。");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
//模拟3个用户
for (int i = 0; i < 20; i++) {
final int user = i + 1;
Runnable r = () -> {
//模拟每个人来的时间不一样
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println(user + "到达聚餐点,当前已有" + (cb.getNumberWaiting() + 1) + "人达到");
//阻塞
cb.await();
if (user == 1) { //打印一句
System.out.println("拍照结束,开始吃饭...");
}
Thread.sleep((long) (Math.random() * 10000));
System.out.println(user + "吃完饭..准备回家.");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
threadPool.execute(r);
}
threadPool.shutdown();
}