java并发包——可重用栅栏(CyclicBarrier)

1. CyclicBarrier的介绍与源码分析

CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。线程进入屏障通过CyclicBarrier的await()方法。

CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction这个Runnable对象,方便处理更复杂的业务场景。

构造函数

1
2
3
4
5
6
public  CyclicBarrier( int  parties) {
     this (parties,  null );
}
public  int  getParties() {
     return  parties;
}

实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用CyclicBarrier的await方法时,将剩余拦截的线程数减1,然后判断剩余拦截数是否为0,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁,接着先从await方法返回,再从CyclicBarrier的await方法中返回。

await源码

1
2
3
4
5
6
7
public  int  await()  throws  InterruptedException, BrokenBarrierException {
     try  {
         return  dowait( false , 0L);
     catch  (TimeoutException toe) {
         throw  new  Error(toe);  // cannot happen
     }
}

dowait源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
private  int  dowait( boolean  timed,  long  nanos)
     throws  InterruptedException, BrokenBarrierException,
            TimeoutException {
     final  ReentrantLock lock =  this .lock;
     lock.lock();
     try  {
         final  Generation g = generation;
 
         if  (g.broken)
             throw  new  BrokenBarrierException();
 
         if  (Thread.interrupted()) {
             breakBarrier();
             throw  new  InterruptedException();
         }
 
         int  index = --count;
         if  (index ==  0 ) {   // tripped
             boolean  ranAction =  false ;
             try  {
                 final  Runnable command = barrierCommand;
                 if  (command !=  null )
                     command.run();
                 ranAction =  true ;
                 nextGeneration();
                 return  0 ;
             finally  {
                 if  (!ranAction)
                     breakBarrier();
             }
         }
 
         // loop until tripped, broken, interrupted, or timed out
         for  (;;) {
             try  {
                 if  (!timed)
                     trip.await();
                 else  if  (nanos > 0L)
                     nanos = trip.awaitNanos(nanos);
             catch  (InterruptedException ie) {
                 if  (g == generation && ! g.broken) {
                     breakBarrier();
                     throw  ie;
                 else  {
                     // We're about to finish waiting even if we had not
                     // been interrupted, so this interrupt is deemed to
                     // "belong" to subsequent execution.
                     Thread.currentThread().interrupt();
                 }
             }
 
             if  (g.broken)
                 throw  new  BrokenBarrierException();
 
             if  (g != generation)
                 return  index;
 
             if  (timed && nanos <= 0L) {
                 breakBarrier();
                 throw  new  TimeoutException();
             }
         }
     finally  {
         lock.unlock();
     }
}

当最后一个线程到达屏障点,也就是执行dowait方法时,会在return 0 返回之前调用finally块中的breakBarrier方法。

breakBarrier源代码

1
2
3
4
5
private  void  breakBarrier() {
     generation.broken =  true ;
     count = parties;
     trip.signalAll();
}

CyclicBarrier主要用于一组线程之间的相互等待,而CountDownLatch一般用于一组线程等待另一组些线程。实际上可以通过CountDownLatch的countDown()和await()来实现CyclicBarrier的功能。即 CountDownLatch中的countDown()+await() = CyclicBarrier中的await()。注意:在一个线程中先调用countDown(),然后调用await()。

其它方法:CycliBarrier对象可以重复使用,重用之前应当调用CyclicBarrier对象的reset方法。

reset源码

1
2
3
4
5
6
7
8
9
10
public  void  reset() {
     final  ReentrantLock lock =  this .lock;
     lock.lock();
     try  {
         breakBarrier();    // break the current generation
         nextGeneration();  // start a new generation
     finally  {
         lock.unlock();
     }
}

2. 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package  javalearning;
 
import  java.util.Random;
import  java.util.concurrent.BrokenBarrierException;
import  java.util.concurrent.CyclicBarrier;
import  java.util.concurrent.ExecutorService;
import  java.util.concurrent.Executors;
 
public  class  CyclicBarrierDemo {
     private  CyclicBarrier cb =  new  CyclicBarrier( 4 );
     private  Random rnd =  new  Random();
     
     class  TaskDemo  implements  Runnable{
         private  String id;
         TaskDemo(String id){
             this .id = id;
         }
         @Override
         public  void  run(){
             try  {
                 Thread.sleep(rnd.nextInt( 1000 ));
                 System.out.println( "Thread "  + id +  " will wait" );
                 cb.await();
                 System.out.println( "-------Thread "  + id +  " is over" );
             catch  (InterruptedException e) {
             catch  (BrokenBarrierException e) {
             }
         }
     }
     
     public  static  void  main(String[] args){
         CyclicBarrierDemo cbd =  new  CyclicBarrierDemo();
         ExecutorService es = Executors.newCachedThreadPool();
         es.submit(cbd. new  TaskDemo( "a" ));
         es.submit(cbd. new  TaskDemo( "b" ));
         es.submit(cbd. new  TaskDemo( "c" ));
         es.submit(cbd. new  TaskDemo( "d" ));
         es.shutdown();
     }
}

在这个示例中,我们创建了四个线程a、b、c、d,这四个线程提交给了线程池。四个线程不同时间到达cb.await()语句,当四个线程都输出“Thread x will wait”以后才会输出“Thread x is over”。

运行结果

Thread d will wait

Thread a will wait

Thread c will wait

Thread b will wait

-------Thread b is over

-------Thread d is over

-------Thread a is over

-------Thread c is over

原文

https://www.cnblogs.com/nullzx/p/5271964.html

猜你喜欢

转载自www.cnblogs.com/yadongliang/p/12469162.html
今日推荐