CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。

若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。 



示例用法:下面是一个在并行分解设计中使用 barrier 的例子:  

class Solver {
   final int N;
   final float[][] data;
   final CyclicBarrier barrier;
   
   class Worker implements Runnable {
     int myRow;
     Worker(int row) { myRow = row; }
     public void run() {
       while (!done()) {
         processRow(myRow);

         try {
           barrier.await(); 
         } catch (InterruptedException ex) { 
return
         } catch (BrokenBarrierException ex) { 
return
         }
       }
     }
   }

   public Solver(float[][] matrix) {
     data = matrix;
     N = matrix.length;
     barrier = new CyclicBarrier(N, 
                                 new Runnable() {
                                   public void run() { 
                                     mergeRows(); 
                                   }
                                 });
     for (int i = 0; i < N; ++i) 
       new Thread(new Worker(i)).start();

     waitUntilDone();
   }
 }

在这个例子中,每个 worker 线程处理矩阵的一行,在处理完所有的行之前,该线程将一直在屏障处等待。
 处理完所有的行之后,将执行所提供的 Runnable 屏障操作,并合并这些行。
  如果合并者确定已经找到了一个解决方案,那么 done() 将返回 true,所有的 worker 线程都将终止。 
如果屏障操作在执行时不依赖于正挂起的线程,则线程组中的任何线程在获得释放时都能执行该操作。
为方便此操作,每次调用 await() 都将返回能到达屏障处的线程的索引。然后,您可以选择哪个线程应该执行屏障操作,例如: 
  if (barrier.await() == 0) {
     // log the completion of this iteration
   }对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。 

内存一致性效果:线程中调用 await() 之前的操作 happen-before 那些是屏障操作的一部份的操作,后者依次 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。 

(1)await

public int await()   throws InterruptedException, BrokenBarrierException在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 
如果当前线程不是将到达的最后一个线程,出于调度目的,将禁用它,且在发生以下情况之一前,该线程将一直处于休眠状态: 
最后一个线程到达;或者 
其他某个线程中断当前线程;或者 
其他某个线程中断另一个等待线程;或者 
其他某个线程在等待 barrier 时超时;或者 
其他某个线程在此 barrier 上调用 reset()。 
如果当前线程: 

在进入此方法时已经设置了该线程的中断状态;或者 
在等待时被中断 
则抛出 InterruptedException,并且清除当前线程的已中断状态。 
如果在线程处于等待状态时 barrier 被 reset(),或者在调用 await 时 barrier 被损坏,抑或任意一个线程正处于等待状态,则抛出 BrokenBarrierException 异常。 

如果任何线程在等待时被 中断,则其他所有等待线程都将抛出 BrokenBarrierException 异常,并将 barrier 置于损坏状态。 

如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。
如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。 

返回:
到达的当前线程的索引,其中,索引 getParties() – 1 指示将到达的第一个线程,零指示最后一个到达的线程 
抛出: 
InterruptedException – 如果当前线程在等待时被中断 
BrokenBarrierException – 如果另一个 线程在当前线程等待时被中断或超时,或者重置了 barrier,或者在调用 await 时 barrier 被损坏,抑或由于异常而导致屏障操作(如果存在)失败。

(2)getNumberWaiting


public int getNumberWaiting()返回当前在屏障处等待的参与者数目。此方法主要用于调试和断言。 

返回:
当前阻塞在 await() 中的参与者数目。

应用实例:

package com.itm.thread;

import java.util.Random;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {
    public static void main(String[] args) {
        final CyclicBarrier cb = new CyclicBarrier(3); // 这个团队中共有3个队员,即需要3个线程
        ExecutorService es = Executors.newFixedThreadPool(3); // 在线程池中放入三个线程
        for (int i = 0; i < 3; i++) { // 开启三个任务
            es.execute(new Runnable() {

                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(new Random().nextInt(5000));
                            System.out.print(Thread.currentThread().getName()
                                    + "已到达集合点" + (i + 1) + ",现在共有"
                                    + (cb.getNumberWaiting() + 1) + "个线程到达");
                            // 如果有2个线程已经在等待,那么最后一个线程到达后就可以一起开始后面操作
                            if (cb.getNumberWaiting() + 1 == 3) {
                                System.out.println(",全部到齐,出发去下一个目标");
                            } else {
                                System.out.println(",正在等待");
                            }
                            cb.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
        es.shutdown();
    }
}

运行结果:

pool-1-thread-3已到达集合点1,现在共有1个线程到达,正在等待
pool-1-thread-2已到达集合点1,现在共有2个线程到达,正在等待
pool-1-thread-1已到达集合点1,现在共有3个线程到达,全部到齐,出发去下一个目标
pool-1-thread-3已到达集合点2,现在共有1个线程到达,正在等待
pool-1-thread-1已到达集合点2,现在共有2个线程到达,正在等待
pool-1-thread-2已到达集合点2,现在共有3个线程到达,全部到齐,出发去下一个目标
pool-1-thread-1已到达集合点3,现在共有1个线程到达,正在等待
pool-1-thread-2已到达集合点3,现在共有2个线程到达,正在等待
pool-1-thread-3已到达集合点3,现在共有3个线程到达,全部到齐,出发去下一个目标
pool-1-thread-1已到达集合点4,现在共有1个线程到达,正在等待
pool-1-thread-2已到达集合点4,现在共有2个线程到达,正在等待
pool-1-thread-3已到达集合点4,现在共有3个线程到达,全部到齐,出发去下一个目标
pool-1-thread-2已到达集合点5,现在共有1个线程到达,正在等待
pool-1-thread-3已到达集合点5,现在共有2个线程到达,正在等待
pool-1-thread-1已到达集合点5,现在共有3个线程到达,全部到齐,出发去下一个目标