一、CyclicBarrier
CyclicBarrier–回环屏障:有点类似于CountDownLatch,但是比CountDownLatch更厉害一点。CountDownLatch是一次性的计数器,计数到0就会立刻返回。而CyclicBarrier的计数器达到0了之后可以重用。
二、源码分析
2.1 继承关系
无
2.2 属性
/** 可以看出基于独占锁实现 */
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();
/**
* 计数器,count为0代表所有线程达到屏障点
*/
private int count;
还有一个静态内部类,只有一个boolean变量,用来纪录当前屏障是否被打破
private static class Generation {
boolean broken = false;
}
2.3 构造方法
public CyclicBarrier(int parties) {
//这个构造方法使用了两个参数的构造方法
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
//传入的int值小于0,则抛异常
if (parties <= 0) throw new IllegalArgumentException();
//赋值,此时parties和count相等。后面的计数操作都是改变count
this.parties = parties;
this.count = parties;
//传入的任务
this.barrierCommand = barrierAction;
}
2.4 常用方法
2.4.1 await()方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
//调用了dowait方法
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
//传入参数false,0L
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();
}
//index==0表示所有线程达到屏障点,执行初始化时传递的任务
int index = --count;
//如果是0
if (index == 0) { // tripped
boolean ranAction = false;
try {
//执行构造时传递的任务,开始执行
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//唤醒所有线程,开始新的一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//计数器不为0则执行此循环
for (;;) {
try {
//传入的是false,非定时模式
if (!timed)
//条件等待,进入条件阻塞队列
trip.await();
else if (nanos > 0L)
//否则定时等待
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();
//g已换代,返回线程数
if (g != generation)
return index;
//超时,打破屏障,唤醒所有线程
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//释放屏障
lock.unlock();
}
}
下一代
private void nextGeneration() {
// 唤醒条件队列所有的线程
trip.signalAll();
// 重新设置计数器
count = parties;
//设置新的generation
generation = new Generation();
}
打破屏障
private void breakBarrier() {
//打破标志
generation.broken = true;
//计数器置为parties
count = parties;
//唤醒所有线程
trip.signalAll();
}
CyclicBarrier的方法基本上就这些了
三、总结
当一个线程使用dowait()方法时,首先获取独占锁。然后将创建CyclicBarrier时传入的参数-1,此时后面的线程全部阻塞。-1后如果不等0,则当前线程进入条件队列阻塞挂起,并释放锁。其他线程则争夺锁,直到使用dowait方法重复上述过程
当计数器==0时,最后一个线程会执行创建时传入的任务(如果有),然后唤醒所有的线程,并充值CyclicBarrier,继续运行
做个示例:
package com.cf.test.lock;
import java.util.concurrent.*;
public class TestDemo3 {
static int count = 1;
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(2, () -> {
System.out.println("关卡任务完成了");
});
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(() -> {
try {
System.out.println("任务1-1完成");
barrier.await();
System.out.println("任务2-1完成");
barrier.await();
System.out.println("任务3-1完成");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
service.submit(() -> {
try {
System.out.println("任务1-2完成");
barrier.await();
System.out.println("任务2-2完成");
barrier.await();
System.out.println("任务3-1完成");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
service.shutdown();
if(service.awaitTermination(1, TimeUnit.MINUTES)){
System.out.println("已通关");
}
}
}
结果: