CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。 CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
构造方法摘要 | |
---|---|
CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在每个 barrier 上执行预定义的操作。 |
|
CyclicBarrier(int parties,Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行 |
方法摘要 | |
---|---|
int |
await() 在所有 参与者 都已经在此 barrier 上调用await 方法之前,将一直等待。 |
int |
await(long timeout,TimeUnit unit) 在所有 参与者 都已经在此屏障上调用await 方法之前,将一直等待。 |
int |
getNumberWaiting() 返回当前在屏障处等待的参与者数目。 |
int |
getParties() 返回要求启动此 barrier 的参与者数目。 |
boolean |
isBroken() 查询此屏障是否处于损坏状态。 |
void |
reset() 将屏障重置为其初始状态 |
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
public class Worker implements Runnable {
final static SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CyclicBarrier barrier;
int workTime;
public Worker(int workTime, CyclicBarrier barrier) {
this.barrier = barrier;
this.workTime = workTime;
}
public void run() {
try {
System.out.println(Thread.currentThread().getName()
+ " :等待: " + sdf.format(new Date()));
// 模拟工作
Thread.sleep(workTime);
// 到此如果没有达到公共屏障点,则该线程处于等待状态,
// 如果达到公共屏障点则所有处于等待的线程都继续往下运行
barrier.await();
} catch (InterruptedException
| BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " :完成: " + sdf.format(new Date()));
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
// 即使传入时间不一样,也会等待时间长的完成才能继续进行
Worker w1 = new Worker(3000, barrier);
Worker w2 = new Worker(5000, barrier);
Worker w3 = new Worker(2000, barrier);
new Thread(w1, "线程1").start();
new Thread(w2, "线程2").start();
new Thread(w3, "线程3").start();
}
}
运行结果
线程3 :等待: 2017-04-15 12:15:17
线程1 :等待: 2017-04-15 12:15:17
线程2 :等待: 2017-04-15 12:15:17
线程3 :完成: 2017-04-15 12:15:22
线程2 :完成: 2017-04-15 12:15:22
线程1 :完成: 2017-04-15 12:15:22
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction,
public class Worker implements Runnable {
final static SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CyclicBarrier barrier;
int workTime;
public Worker(int workTime, CyclicBarrier barrier) {
this.barrier = barrier;
this.workTime = workTime;
}
public void run() {
try {
System.out.println(Thread.currentThread().getName()
+ " :等待: " + sdf.format(new Date()));
// 模拟工作
Thread.sleep(workTime);
// 到此如果没有达到公共屏障点,则该线程处于等待状态,
// 如果达到公共屏障点则所有处于等待的线程都继续往下运行
barrier.await();
} catch (InterruptedException
| BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " :完成: " + sdf.format(new Date()));
}
public static void main(String[] args) {
/////////
CyclicBarrier barrier = new CyclicBarrier(3,new Runnable() {
@Override
public void run() {
System.out.println("这是Runnable barrierAction……");
}
});
// 即使传入时间不一样,也会等待时间长的完成才能继续进行
Worker w1 = new Worker(3000, barrier);
Worker w2 = new Worker(5000, barrier);
Worker w3 = new Worker(2000, barrier);
new Thread(w1, "线程1").start();
new Thread(w2, "线程2").start();
new Thread(w3, "线程3").start();
}
}
结果
线程2 :等待: 2017-04-15 12:18:42
线程1 :等待: 2017-04-15 12:18:42
线程3 :等待: 2017-04-15 12:18:42
这是Runnable barrierAction……
线程1 :完成: 2017-04-15 12:18:47
线程3 :完成: 2017-04-15 12:18:47
线程2 :完成: 2017-04-15 12:18:47
除了常见的异常,CyclicBarrier.await() 方法会抛出一个独有的 BrokenBarrierException。这个异常发生在当某个线程在等待本 CyclicBarrier 时被中断或超时或被重置时,其它同样在这个 CyclicBarrier 上等待的线程便会受到 BrokenBarrierException。意思就是说,同志们,别等了,有个小伙伴已经挂了,咱们如果继续等有可能会一直等下去,所有各回各家吧。CyclicBarrier.await() 方法带有返回值,用来表示当前线程是第几个到达这个 Barrier 的线程。
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。
注意CountDownLatch是阻塞主线程,而CyclicBarrier阻塞子线程
模拟循环使用
public class Worker implements Runnable {
final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CyclicBarrier barrier;
int workTime;
public Worker(int workTime, CyclicBarrier barrier) {
this.barrier = barrier;
this.workTime = workTime;
}
public void run() {
try {
System.out.println(Thread.currentThread().getName()
+ " :等待: " + sdf.format(new Date()));
// 模拟工作
Thread.sleep(workTime);
// 到此如果没有达到公共屏障点,则该线程处于等待状态,
// 如果达到公共屏障点则所有处于等待的线程都继续往下运行
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " :完成: " + sdf.format(new Date()));
}
public static void main(String[] args) throws InterruptedException {
/////////
CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("这是Runnable barrierAction……");
}
});
// 即使传入时间不一样,也会等待时间长的完成才能继续进行
Worker w1 = new Worker(3000, barrier);
Worker w2 = new Worker(4000, barrier);
Worker w3 = new Worker(2000, barrier);
new Thread(w1, "线程1").start();
new Thread(w2, "线程2").start();
new Thread(w3, "线程3").start();
//模拟循环使用
Thread.sleep(6000);
System.out.println("---------------");
barrier.reset();
new Thread(w1, "线程1").start();
new Thread(w2, "线程2").start();
new Thread(w3, "线程3").start();
}
}
线程1 :等待: 2017-04-15 12:30:30
线程3 :等待: 2017-04-15 12:30:30
线程2 :等待: 2017-04-15 12:30:30
这是Runnable barrierAction……
线程2 :完成: 2017-04-15 12:30:34
线程3 :完成: 2017-04-15 12:30:34
线程1 :完成: 2017-04-15 12:30:34
---------------
线程2 :等待: 2017-04-15 12:30:36
线程1 :等待: 2017-04-15 12:30:36
线程3 :等待: 2017-04-15 12:30:36
这是Runnable barrierAction……
线程2 :完成: 2017-04-15 12:30:40
线程3 :完成: 2017-04-15 12:30:40
线程1 :完成: 2017-04-15 12:30:40
参考: 线程同步工具之CountDownLatch