考虑这样一个场景,假设你有多个线程,这些线程并行执行,你设置了一个目的地,你想做到的是,无论哪个线程先到达目的地,都得等待其它线程到达,当指定数量的线程都到达目的地时,才允许它们继续一起往下执行;
这个时候就是CyclicBarrier的用武之地了!
//一组任务并行执行,这些任务之间互相等待,直到指定的线程数都到达某一个屏障点(就是调用await的地方) ,才允许往下执行
//就像是一堆马,无论它们谁先打到围栏处,都得先等待其它马到达,当全部到达后,再打开围栏,允许它们继续行走;
//CyclicBarrier与CountDownLatch不同的是,它可以一直循环下去,下面需要等待3个线程执行完毕,当3个线程执行完毕后,它放行一次,接着自动重新等待3个线程...
//构造函数中可以传入一个线程,也可以不传,当屏障被打开时,该线程将先获得调用!
public static void cyclicBarrier() throws InterruptedException, BrokenBarrierException{
final CyclicBarrier cb = new CyclicBarrier(3,new Runnable() {
public void run() {
//当屏障被打开时,该线程将先获得调用!
System.out.println("屏障点被打开!");
}
});
ExecutorService exec =Executors.newCachedThreadPool();
for (int i = 0; i < 6; i++) {
exec.execute(new Runnable() {
public void run() {
try {
System.out.println("准备执行一次任务");
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(2000));
System.out.println("等待其他任务执行");
//屏障点,当到达屏障点的线程数量满足CyclicBarrier的初始化数量,屏障将打开,然后继续等待,
//在我们这六个线程中,当有3个线程调用了cb.await()时,屏障打开,3个线程放行;而后3个线程需要继续等待屏障打开
cb.await();
System.out.println("任务执行完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
exec.shutdown();
}
准备执行一次任务
准备执行一次任务
准备执行一次任务
准备执行一次任务
准备执行一次任务
准备执行一次任务
等待其他任务执行
等待其他任务执行
等待其他任务执行
屏障点被打开!
任务执行完毕
任务执行完毕
任务执行完毕
等待其他任务执行
等待其他任务执行
等待其他任务执行
屏障点被打开!
任务执行完毕
任务执行完毕
任务执行完毕
***************************************************************************************************
我们创建了一个CyclicBarrier,构造参数3代表着它每次等待的线程数是3个,另一个构造参数则是当有3个线程执行到达屏障点时,它将获得执行;
从输出中我们看到,先是6个线程都打印出【准备执行一次任务】,然后它们被sleep阻塞,首先是3个线程到屏障点,然后屏障被打开,另外3个线程才得以执行,且这三个线程到达屏障点时又一次打印出了【屏障点被打开!】,说明CyclicBarrier是可以一直循环复用下去的;
CyclicBarrier还有些方法可以用,比如在运行时改变要等待的线程数,这个在CountDownLatch是不能改变的;具体参见API;