Java并发工具类之同步屏障CyclicBarrier
CyclicBarrier的字面意思是可以循环使用的Barrier,它要做的事情是让一个线程到达一个Barrier的时候被阻塞,直到最后一个线程到达Barrier,屏障才会放开,所有被Barrier拦截的线程才会继续运行。
CyclicBarrier的默认的构造器是CyclicBarrier(int parties),参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。示例代码如下:
public class CyclicBarrierTest { private static CyclicBarrier barrier = new CyclicBarrier(2); public static void main(String[] args) throws BrokenBarrierException, InterruptedException { new Thread(new Runnable() { @Override public void run() { try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.print("thread1"); } }).start(); barrier.await(); System.out.println("mainThread"); } }
因为子线程和主线程的调度由CPU决定,每个线程都有可能优先执行,因此会产生两种不同的结果,可能的结果如下:
mainThread
thread1
如果把CyclicBarrier的Barrier的个数由2改为3,则主线程和子线程会永远等待,因为没有第三个线程执行await方法,即没有第三个线程到达屏障,因此之前到达屏障的两个线程都不会继续执行。
CyclicBarrier还提供了一个高级的构造器,CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障后,优先执行barrierAction线程里面的逻辑。比如有如下的场景,在所有的线程都处理完成后,打印“All Done”,可以参考如下代码:
public class CyclicBarrierTest2 { private static CyclicBarrier barrier = new CyclicBarrier(2,new DoneThread()); public static void main(String[] args) throws BrokenBarrierException, InterruptedException { new Thread(new Runnable() { @Override public void run() { System.out.println("thread1"); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }).start(); System.out.println("mainThread"); barrier.await(); } static class DoneThread implements Runnable{ @Override public void run() { System.out.println("All Done"); } } }
输出结果如下:
mainThread
thread1
All Done
因此CyclicBarrier常用于处理,多线程计算最后合并计算结果的场景。
CyclicBarrier和CountDownLatch的区别:
1.CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset方法重置,因此CyclicBarrier可以处理线程计算出错时重复计算的场景。
2.CyclicBarrier还提供了别的有用的方法,如getNumberWaiting可以获得等待的线程数,isBroken方法可以了解阻塞的线程是否被中断等。