JDK 源码解析 —— CyclicBarrier

一. 简介
CyclicBarrier 是一个让一系列线程集合互相等待直到一个公共屏障点(barrier point)的同步辅助工具。这个屏障被称为循环屏障,是因为它可以在等待线程释放后被重用。
 
CyclicBarrier 支持一个可选的 Runnable 命令,在最后一个线程到达后执行一次 Runnable 命令。
 
 
二. 简单使用示例

 CyclicBarrier(3) 等到 3 个线程都到了,这个对象还可以重用,而 CountDownLatch 则不能重用,从 Cyclic 名字就可以看出这个类对象可以循环使用

 

[java] view plain copy
 
 print?
  1. public class CyclicBarrierTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         ExecutorService service = Executors.newCachedThreadPool();  
  5.         final  CyclicBarrier cb = new CyclicBarrier(3);//创建CyclicBarrier对象并设置3个公共屏障点  
  6.         for(int i=0;i<3;i++){  
  7.             Runnable runnable = new Runnable(){  
  8.                     public void run(){  
  9.                     try {  
  10.                         Thread.sleep((long)(Math.random()*10000));  
  11.                         System.out.println("线程" + Thread.currentThread().getName() +  
  12.                                 "即将到达集合地点1,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");  
  13.                         cb.await();//到此如果没有达到公共屏障点,则该线程处于等待状态,如果达到公共屏障点则所有处于等待的线程都继续往下运行  
  14.   
  15.                         Thread.sleep((long)(Math.random()*10000));  
  16.                         System.out.println("线程" + Thread.currentThread().getName() +  
  17.                                 "即将到达集合地点2,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");  
  18.                         cb.await();  
  19.                         Thread.sleep((long)(Math.random()*10000));  
  20.                         System.out.println("线程" + Thread.currentThread().getName() +  
  21.                                 "即将到达集合地点3,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");  
  22.                         cb.await();  
  23.                     } catch (Exception e) {  
  24.                         e.printStackTrace();  
  25.                     }  
  26.                 }  
  27.             };  
  28.             service.execute(runnable);  
  29.         }  
  30.         service.shutdown();  
  31.     }  
  32. }  

 

 

三. CyclicBarrier 作用图示
让所有线程都运行到同一个点(屏障点)后,再继续运行

 

 

四. 代码解析
  1. 重要变量
[java] view plain copy
 
 print?
  1. // 每次对栅栏的使用可以表现为一个 generation 实例。当条件 trip 改变或者重置 generation 也会  
  2.     // 随之改变。可以有多个 generation 和使用栅栏的线程关联,但是只有一个可以获得锁。  
  3.     private static class Generation {  
  4.         boolean broken = false;  
  5.     }  
  6.   
  7.     /** 守护栅栏入口的锁 */  
  8.     private final ReentrantLock lock = new ReentrantLock();  
  9.     /** 等待条件,直到所有线程到达栅栏 */  
  10.     private final Condition trip = lock.newCondition();  
  11.     /** 要屏障的线程数 */  
  12.     private final int parties;  
  13.     /* 当线程都到达栅栏,运行的 Runnable */  
  14.     private final Runnable barrierCommand;  
  15.     /** The current generation */  
  16.     private Generation generation = new Generation();  
  17.   
  18.     //还要等待多少个线程到达。线程到达屏障点就减去 1。  
  19.     //每次新建 generation 的时候或者屏障 broken,count重新设置为 parties 参数值  
  20.     private int count;  



 

 

  1. await() 方法:等待到所有参与的线程都到达屏障点。如果当前线程不是最后一个到达的,当前线程停止运行,进入睡眠,直到以下几种情况发生
  • 最后的线程到达
  • 其他线程中断当前线程
  • 其他线程中断中断等待线程中的一条
  • 在等待所有线程到达屏障前有线程超时
  • 其他线程在此屏障中调用 reset(将屏障设置为初始状态)
 
如果当前线程:
  • 设置了中断状态
  • 在等待时中断
那么,就会抛出 InterruptedException,并且当前线程中断状态被清除。
 
如果在任何线程等待过程中屏障被重置(即调用 reset() 方法),那么所有的线程都会抛出 BrokenBarrierException,并且这个屏障置于 broken 状态。
 
如果当前线程是最后一个到达屏障的线程,并且屏障的构造器传入了 Runnable 参数,那么在其他线程执行前,先执行 Runnable。如果在屏障运行中发生了异常,那么异常会在当前线程中被传播,屏障将被置于 broken 状态。
 
返回值:返回当前线程到达的下标 
[java] view plain copy
 
 print?
  1. public int await() throws InterruptedException, BrokenBarrierException {  
  2.     try {  
  3.         return dowait(false, 0L);  
  4.     } catch (TimeoutException toe) {  
  5.         throw new Error(toe); // cannot happen;  
  6.     }  
  7. }  
  8.   
  9. private int dowait(boolean timed, long nanos)  
  10.     throws InterruptedException, BrokenBarrierException,  
  11.            TimeoutException {  
  12.     final ReentrantLock lock = this.lock;  
  13.     lock.lock();// 加了锁,以下操作为线程安全操作  
  14.     try {  
  15.         final Generation g = generation;  
  16.   
  17.         if (g.broken)  // 如果屏障状态 broken,则抛出屏障 broken 异常  
  18.             throw new BrokenBarrierException();  
  19.   
  20.         if (Thread.interrupted()) {  
  21.             breakBarrier();  
  22.             throw new InterruptedException();  
  23.         }  
  24.   
  25.        int index = --count;  
  26.        if (index == 0) {  // tripped 说明是最后一个到达的线程  
  27.            boolean ranAction = false;  
  28.            try {  
  29.                final Runnable command = barrierCommand;  
  30.                if (command != null) // 如果有 Runnable,先执行  
  31.                    command.run();  
  32.                ranAction = true;  
  33.                nextGeneration();// 唤醒 Condition 队列的所有线程,既然是 Cyclic 的,所以也会重置状态以便重用屏障,这是和 CountDownLatch 的区别  
  34.                return 0;  
  35.            } finally {  
  36.                if (!ranAction)  
  37.                    breakBarrier();  
  38.            }  
  39.        }  
  40.   
  41.         // loop until tripped, broken, interrupted, or timed out  
  42.         for (;;) {// 如果不是最后一个到达的线程,就进入循环等待  
  43.             try {  
  44.                 if (!timed)  
  45.                     trip.await();  
  46.                 else if (nanos > 0L)  
  47.                     nanos = trip.awaitNanos(nanos);  
  48.             } catch (InterruptedException ie) {  
  49.                 if (g == generation && ! g.broken) {  
  50.                     breakBarrier();  
  51.                     throw ie;  
  52.                 } else {  
  53.                     // We're about to finish waiting even if we had not  
  54.                     // been interrupted, so this interrupt is deemed to  
  55.                     // "belong" to subsequent execution.  
  56.                     Thread.currentThread().interrupt();  
  57.                 }  
  58.             }  
  59.   
  60.             if (g.broken)  
  61.                 throw new BrokenBarrierException();  
  62.   
  63.             if (g != generation)  
  64.                 return index;  
  65.   
  66.             if (timed && nanos <= 0L) {  
  67.                 breakBarrier();  
  68.                 throw new TimeoutException();  
  69.             }  
  70.         }  
  71.     } finally {  
  72.         lock.unlock();  
  73.     }  
  74. }  

 

 

 

 

五. 总结
CyclicBarrier 是利用了 Condition 接口,定义了一个叫做 trip 的 Condition,当所有线程到达后线程才能从 Condition 队列中移到 AQS 的等待队列继续运行。关于 Condition,可以参考博主的另一篇博文:http://blog.csdn.net/wenniuwuren/article/details/51447767
 
 
 
六. 参考资料
JDK 7 源码 

posted on 2017-07-05 18:02  alex5211314  阅读(118)  评论(0编辑  收藏  举报

导航