CyclicBarrier使用
最简单使用
public class CyclicBarrierTest1 {
static CyclicBarrier c = new CyclicBarrier(7,()->{
System.out.println("集齐七龙珠 召唤神龙");
});
public static void main(String[] args) {
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println("收集到第" + temp + "颗龙珠!");
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
结果
收集到第0颗龙珠!
收集到第1颗龙珠!
收集到第2颗龙珠!
收集到第3颗龙珠!
收集到第4颗龙珠!
收集到第5颗龙珠!
收集到第6颗龙珠!
集齐七龙珠 召唤神龙
可以看到 这个和countdownlatch类似 ,7个子线程运行结束后,打印“召唤神龙”
但是和 countdownlatch不同在于
CyclicBarrier | countdownlatch |
---|---|
到达节点的方法是使用 新的线程处理 | 在主线程中写 |
可以循环使用 | 使用一次 |
会阻塞子线程,await()集合了countDownlatch的功能 | countDownLatch.countDown(); 只会扣减 countDownLatch.await();只做阻塞 |
循环栅栏中 对 循环的理解
public class CyclicBarrierTest {
static CyclicBarrier c = new CyclicBarrier(7,()->{
System.out.println("集齐七龙珠 召唤神龙");
});
public static void main(String[] args) {
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println("收集到第" + temp + "颗龙珠!");
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("第二次收集到第" + temp + "颗龙珠!");
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
收集到第0颗龙珠!
收集到第1颗龙珠!
收集到第2颗龙珠!
收集到第3颗龙珠!
收集到第4颗龙珠!
收集到第5颗龙珠!
收集到第6颗龙珠!
集齐七龙珠 召唤神龙
第二次收集到第6颗龙珠!
第二次收集到第1颗龙珠!
第二次收集到第2颗龙珠!
第二次收集到第0颗龙珠!
第二次收集到第3颗龙珠!
第二次收集到第4颗龙珠!
第二次收集到第5颗龙珠!
集齐七龙珠 召唤神龙
可以看到每次收集到 都会执行一次 回调函数(barrierAction)的方法。
这就是 循环的意思。
实现原理
构造函数
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;
}
核心方法
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重置,将generation重置
// 唤醒之前等待的线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
// 如果执行栅栏任务的时候失败了,就将损坏状态设置为tru
breakBarrier();
}
}
for (;;) {
try {
// 如果没有时间限制,则直接等待,直到被唤醒
if (!timed)
trip.await();
else if (nanos > 0L)
// 如果有时间限制,则等待指定时间
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
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();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
可以看到 每次当 count(栅栏数-到达栅栏点的线程数)为0时,都会去调用barrierCommand的run方法,
注意
是最后一个到达栅栏点的 方法调用barrierCommand的run方法,所以这里是没有开启线程的。
reset()理解
会重置到达栅栏点的线程数,
public class CyclicBarrierTest {
static CyclicBarrier c = new CyclicBarrier(6,()->{
System.out.println("集齐七龙珠 召唤神龙");
});
public static void main(String[] args) {
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println("收集到第" + temp + "颗龙珠!");
if(temp ==2){
c.reset();
}
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
System.out.println(temp +"出现InterruptedException异常");
e.printStackTrace();
}
}).start();
}
}
}
收集到第0颗龙珠!
收集到第1颗龙珠!
收集到第2颗龙珠!
收集到第3颗龙珠!
收集到第4颗龙珠!
收集到第5颗龙珠!
收集到第6颗龙珠!
java.util.concurrent.BrokenBarrierException
这里 reset调用后,会使其他 已经在等待的线程出现 BrokenBarrierException异常,所以 建议 :
在使用reset时 在 线程中处理 BrokenBarrierException异常
参考资料:
https://blog.csdn.net/qq_37001674/article/details/88244740
https://zhuanlan.zhihu.com/p/148964094