CyclicBarrier
CyclicBarrier(循环栅栏)
是一个同步辅助类。它允许两个或者多个线程在某个点上进行同步。CyclicBarrier
字面意思是可循环(Cyclic
)使用的屏障(Barrier
).因为该Barrier
在释放等待线程后可以重用(计数器可以反复使用),所以称它为循环的Barrier
。让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。CyclicBarrier
不仅具有CountDownLatch
所具有的功能,CyclicBarrier
还可以用于多线程计算数据,最后合并计算结果的场景。比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务。- 图解
- 主要方法
getNumberWaiting
有几个线程已经到达屏障点isBroken
了解阻塞的线程是否被中断int getParties()
返回要求启动此barrier的参与者数目int await()
在所有参与者都已经在此barrier上调用await方法之前,将一直等待。即每个线程调用此方法告诉CyclicBarrier
已经到达屏障,然后阻塞
CyclicBarrier
对于线程的中断interrupte处理会使用全有或全无的破坏模式,即如果有一个线程由于中断或者超时提前离开了屏障点,其他所有在屏障点等待的线程也会抛出BrokenBarrierException
后者InterruptedException
异常,并且离开屏障点。
CyclicBarrier与CountDownLatch区别
CountDownLatch
的计数器只能使用一次,而CyclicBarrier
的计数器可以使用reset()
方法重置。所以CyclicBarrier
可以处理更复杂的业务场景。比如如果计算发生错误,可以重置技术器,并让线程重新执行一次。CountDownLatch
作用:一个线程或多个线程,等待另一个线程或多线程完成某个事情之后才能继续执行CyclicBarrier
的作用:多个线程之间相互等待,任何一个线程完成之前,所有的线程都必须等待,所以对于CyclicBarrier
来说,重点是多个线程之间任何一个线程没有完成任务,则所有的线程都必须等待。- 与
CountDownLatch
不同,CyclicBarrier
的计数是加法操作。
案例
-
案例一:
@Test public void testCyclicBarrier() { CyclicBarrier cyclicBarrier = new CyclicBarrier(8); ExecutorService service = Executors.newFixedThreadPool(5); service.submit(new Worker(cyclicBarrier, "选手1")); service.submit(new Worker(cyclicBarrier, "选手2")); service.submit(new Worker(cyclicBarrier, "选手3")); service.submit(new Worker(cyclicBarrier, "选手4")); service.submit(new Worker(cyclicBarrier, "选手5")); try { cyclicBarrier.await(); //睡眠,让所有线程执行完毕 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } //参与者数目6,构造方法的值 System.out.println(String.format("参与者:%s", cyclicBarrier.getParties())); //达到屏障点的线程数0 System.out.println(String.format("达到屏障点的线程数:%s",cyclicBarrier.getNumberWaiting())); service.shutdown(); } static class Worker implements Runnable { public final CyclicBarrier cyclicBarrier; public final String name; public Worker(CyclicBarrier cyclicBarrier, String name) { this.cyclicBarrier = cyclicBarrier; this.name = name; } @Override public void run() { try { Thread.sleep(1000 * (new Random().nextInt(5))); System.out.println(String.format("%s准备完毕,当前达到屏障点的线程数:%s", name, cyclicBarrier.getNumberWaiting())); //barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + "开始"); } } 输出结果: 选手1准备完毕,当前达到屏障点的线程数:1 选手5准备完毕,当前达到屏障点的线程数:2 选手4准备完毕,当前达到屏障点的线程数:3 选手2准备完毕,当前达到屏障点的线程数:3 选手3准备完毕,当前达到屏障点的线程数:5 选手3开始 选手5开始 选手1开始 选手4开始 选手2开始 参与者:6 达到屏障点的线程数:0
-
案例二:多线程分治计算,然后将计算结果合并。
CyclicBarrier(int parties, Runnable barrierAction)
用于在线程达到屏障时,优先执行barrierActionpublic class CyclicBarrierTest3 { @Test public void test() { BankWaterService bankWaterCount = new BankWaterService(); //输出:4 bankWaterCount.count(); LockSupport.park(); } static class BankWaterService implements Runnable { // 创建4个屏障,当屏障开闸时执行barrierAction(处理完之后执行),即执行当前类的run方法 private CyclicBarrier c = new CyclicBarrier(4, this); private ExecutorService executor = Executors.newFixedThreadPool(4); private ConcurrentHashMap<String, Integer> count = new ConcurrentHashMap<>(); private void count() { for (int i = 0; i < 4; i++) { executor.execute(() -> { // 计算 count.put(Thread.currentThread().getName(), 1); try { // 计算完成,插入一个屏障 c.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }); } } public void shutdown() { executor.shutdown(); } @Override public void run() { //汇总计算出的结果 int result = count.values().stream().mapToInt(Integer::intValue).sum(); System.out.println(result); } } }