从字面意思来理解Cyclic(循环的)Barrier(栅栏、屏障),主要的用途在于,让一组线程到达屏障时阻塞,直到最后一个线程也到达屏障时,线程才执行。默认的构造方法为CyclicBarrier(int parties),parties表示线程的数量,需要每个线程都调用await方法,通知已经到达屏障,进行线程阻塞。
需要所有的子线程都完成时,才开始执行主线程。可以用于多线程计算数据,最后合并计算结果的应用场景,比如计算一个Excel中保存的所有订单的总金额,先通过多线程计算每个sheet中的订单总额,最后计算出所有订单的中金额。
CountDownLatch的计数器只能使用一次,而CyclicBarrier可以通过reset方法重置计数器,因此,CyclicBarrier能够处理更加复杂的业务场景,比如如果计算结果出错时,可以通过调用reset方法进行计数器重置,让线程重新进行计算。CyclicBarrier还提供了其他有用的方法,比如getNumberBarriers方法,可以获取当前阻塞的线程数量;isBroken方法,判断阻塞的线程是否中断。
/**
* The lock for guarding barrier entry
* 控制屏障入口的锁
*/
private final ReentrantLock lock = new ReentrantLock();
/**
* Condition to wait on until tripped
* 等待直到被tipped的条件
*/
private final Condition trip = lock.newCondition();
/**
* The number of parties
* 记录线程数量
*/
private final int parties;
/*
* The command to run when tripped
* 该线程来源于高级构造方法CyclicBarrier(int parties, Runnable barrierAction)中进行初始化,
* 用于记录当最后一个线程到达时,优先开始执行的线程
*/
private final Runnable barrierCommand;
/**
* The current generation
* 一个类部类,有一个变量broken用于判断阻塞的线程是否中断,默认false
*/
private Generation generation = new Generation();
/**
* Number of parties still waiting. Counts down from parties to 0
* on each generation. It is reset to parties on each new
* generation or when broken.
* 这个是计录在等待线程的计数器,会做自减
*/
private int count;
主要的核心方法为await方法,代码如下:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
/**
* Main barrier code, covering the various policies.
*/
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) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)//开始运行优先执行的线程
command.run();
ranAction = true;
nextGeneration();//更新屏障的状态,唤醒所有阻塞的线程
return 0;
} finally {
if (!ranAction)
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) {
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();
}
}
运行实例代码如下:
public class CyclicBarrierTest1 {
static CyclicBarrier c = new CyclicBarrier(2);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
c.await();
} catch (Exception e) {
}
System.out.println(2);
}
}
输出
2
1
或者输出
1
2
如果把线程数new CyclicBarrier(2)改写成new CyclicBarrier(3),那么之前两个到达屏障的线程都不会执行,结果不会有输出,因为没有第三个线程到达屏障。
有一个高级构造方法CyclicBarrier(int parties, Runnable barrierAction),用于在最后一个线程到达屏障时,优先执行barrierAction线程,应用于复杂的业务场景。
代码如下:
public class CyclicBarrierTest2 {
static CyclicBarrier c = new CyclicBarrier(2, new A());
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
c.await();
} catch (Exception e) {
}
System.out.println(2);
}
static class A implements Runnable {
@Override
public void run() {
System.out.println(3);
}
}
}
输出
3
2
1
isBroken方法的用法如下:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest3 {
static CyclicBarrier c = new CyclicBarrier(2);
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
}
});
thread.start();
thread.interrupt();
try {
c.await();
} catch (Exception e) {
System.out.println(c.isBroken());
}
}
}
并发工具类(二)同步屏障CyclicBarrier
Java之CyclicBarrier使用