CyclicBarrier用法

字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:

1   public CyclicBarrier(int parties, Runnable barrierAction) {
2   }
3    
4   public CyclicBarrier(int parties) {
5   }

参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:

1   public int await() throws InterruptedException, BrokenBarrierException { };
2   public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

第一个方法比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;

第二个方法是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。

下面举几个例子就明白了:
假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,此时就可以利用CyclicBarrier了:

1   public class Test {
2       public static void main(String[] args) {
3           int N = 4;
4           CyclicBarrier barrier  = new CyclicBarrier(N);
5           for(int i=0;i<N;i++)
6               new Writer(barrier).start();
7       }
8       static class Writer extends Thread{
9           private CyclicBarrier cyclicBarrier;
10          public Writer(CyclicBarrier cyclicBarrier) {
11              this.cyclicBarrier = cyclicBarrier;
12          }
13   
14          @Override
15          public void run() {
16              System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
17              try {
18                  Thread.sleep(5000);      //以睡眠来模拟写入数据操作
19                  System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
20                  cyclicBarrier.await();
21              } catch (InterruptedException e) {
22                  e.printStackTrace();
23              }catch(BrokenBarrierException e){
24                  e.printStackTrace();
25              }
26              System.out.println("所有线程写入完毕,继续处理其他任务...");
27          }
28      }
29  }

执行结果:

1   线程Thread-0正在写入数据...
2   线程Thread-3正在写入数据...
3   线程Thread-2正在写入数据...
4   线程Thread-1正在写入数据...
5   线程Thread-2写入数据完毕,等待其他线程写入完毕
6   线程Thread-0写入数据完毕,等待其他线程写入完毕
7   线程Thread-3写入数据完毕,等待其他线程写入完毕
8   线程Thread-1写入数据完毕,等待其他线程写入完毕
9   所有线程写入完毕,继续处理其他任务...
10  所有线程写入完毕,继续处理其他任务...
11  所有线程写入完毕,继续处理其他任务...
12  所有线程写入完毕,继续处理其他任务...

从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。

当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。

如果说想在所有线程写入操作完之后,进行额外的其他操作可以为CyclicBarrier提供Runnable参数,示例如下:

1   public class Test {
2       public static void main(String[] args) {
3           int N = 4;
4           CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
5               @Override
6               public void run() {
7                   System.out.println("当前线程"+Thread.currentThread().getName());   
8               }
9           });
10   
11          for(int i=0;i<N;i++)
12              new Writer(barrier).start();
13      }
14      static class Writer extends Thread{
15          private CyclicBarrier cyclicBarrier;
16          public Writer(CyclicBarrier cyclicBarrier) {
17              this.cyclicBarrier = cyclicBarrier;
18          }
19   
20          @Override
21          public void run() {
22              System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
23              try {
24                  Thread.sleep(5000);      //以睡眠来模拟写入数据操作
25                  System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
26                  cyclicBarrier.await();
27              } catch (InterruptedException e) {
28                  e.printStackTrace();
29              }catch(BrokenBarrierException e){
30                  e.printStackTrace();
31              }
32              System.out.println("所有线程写入完毕,继续处理其他任务...");
33          }
34      }
35  }

运行结果:

1   线程Thread-0正在写入数据...
2   线程Thread-1正在写入数据...
3   线程Thread-2正在写入数据...
4   线程Thread-3正在写入数据...
5   线程Thread-0写入数据完毕,等待其他线程写入完毕
6   线程Thread-1写入数据完毕,等待其他线程写入完毕
7   线程Thread-2写入数据完毕,等待其他线程写入完毕
8   线程Thread-3写入数据完毕,等待其他线程写入完毕
9   当前线程Thread-3
10  所有线程写入完毕,继续处理其他任务...
11  所有线程写入完毕,继续处理其他任务...
12  所有线程写入完毕,继续处理其他任务...
13  所有线程写入完毕,继续处理其他任务...

从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。

下面看一下为await指定时间的效果:

1   public class Test {
2       public static void main(String[] args) {
3           int N = 4;
4           CyclicBarrier barrier  = new CyclicBarrier(N);
5    
6           for(int i=0;i<N;i++) {
7               if(i<N-1)
8                   new Writer(barrier).start();
9               else {
10                  try {
11                      Thread.sleep(5000);
12                  } catch (InterruptedException e) {
13                      e.printStackTrace();
14                  }
15                  new Writer(barrier).start();
16              }
17          }
18      }
19      static class Writer extends Thread{
20          private CyclicBarrier cyclicBarrier;
21          public Writer(CyclicBarrier cyclicBarrier) {
22              this.cyclicBarrier = cyclicBarrier;
23          }
24   
25          @Override
26          public void run() {
27              System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
28              try {
29                  Thread.sleep(5000);      //以睡眠来模拟写入数据操作
30                  System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
31                  try {
32                      cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
33                  } catch (TimeoutException e) {
34                      // TODO Auto-generated catch block
35                      e.printStackTrace();
36                  }
37              } catch (InterruptedException e) {
38                  e.printStackTrace();
39              }catch(BrokenBarrierException e){
40                  e.printStackTrace();
41              }
42              System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
43          }
44      }
45  }

执行结果:

1   线程Thread-0正在写入数据...
2   线程Thread-2正在写入数据...
3   线程Thread-1正在写入数据...
4   线程Thread-2写入数据完毕,等待其他线程写入完毕
5   线程Thread-0写入数据完毕,等待其他线程写入完毕
6   线程Thread-1写入数据完毕,等待其他线程写入完毕
7   线程Thread-3正在写入数据...
8   java.util.concurrent.TimeoutException
9   Thread-1所有线程写入完毕,继续处理其他任务...
10  Thread-0所有线程写入完毕,继续处理其他任务...
11      at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
12      at java.util.concurrent.CyclicBarrier.await(Unknown Source)
13      at com.cxh.test1.Test$Writer.run(Test.java:58)
14  java.util.concurrent.BrokenBarrierException
15      at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
16      at java.util.concurrent.CyclicBarrier.await(Unknown Source)
17      at com.cxh.test1.Test$Writer.run(Test.java:58)
18  java.util.concurrent.BrokenBarrierException
19      at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
20      at java.util.concurrent.CyclicBarrier.await(Unknown Source)
21      at com.cxh.test1.Test$Writer.run(Test.java:58)
22  Thread-2所有线程写入完毕,继续处理其他任务...
23  java.util.concurrent.BrokenBarrierException
24  线程Thread-3写入数据完毕,等待其他线程写入完毕
25      at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
26      at java.util.concurrent.CyclicBarrier.await(Unknown Source)
27      at com.cxh.test1.Test$Writer.run(Test.java:58)
28  Thread-3所有线程写入完毕,继续处理其他任务...

上面的代码在main方法的for循环中,故意让最后一个线程启动延迟,因为在前面三个线程都达到barrier之后,等待了指定的时间发现第四个线程还没有达到barrier,就抛出异常并继续执行后面的任务。

另外CyclicBarrier是可以重用的,看下面这个例子:

1   public class Test {
2       public static void main(String[] args) {
3           int N = 4;
4           CyclicBarrier barrier  = new CyclicBarrier(N);
5    
6           for(int i=0;i<N;i++) {
7               new Writer(barrier).start();
8           }
9    
10          try {
11              Thread.sleep(25000);
12          } catch (InterruptedException e) {
13              e.printStackTrace();
14          }
15   
16          System.out.println("CyclicBarrier重用");
17   
18          for(int i=0;i<N;i++) {
19              new Writer(barrier).start();
20          }
21      }
22      static class Writer extends Thread{
23          private CyclicBarrier cyclicBarrier;
24          public Writer(CyclicBarrier cyclicBarrier) {
25              this.cyclicBarrier = cyclicBarrier;
26          }
27   
28          @Override
29          public void run() {
30              System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
31              try {
32                  Thread.sleep(5000);      //以睡眠来模拟写入数据操作
33                  System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
34   
35                  cyclicBarrier.await();
36              } catch (InterruptedException e) {
37                  e.printStackTrace();
38              }catch(BrokenBarrierException e){
39                  e.printStackTrace();
40              }
41              System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
42          }
43      }
44  }

执行结果:

1   线程Thread-0正在写入数据...
2   线程Thread-1正在写入数据...
3   线程Thread-3正在写入数据...
4   线程Thread-2正在写入数据...
5   线程Thread-1写入数据完毕,等待其他线程写入完毕
6   线程Thread-3写入数据完毕,等待其他线程写入完毕
7   线程Thread-2写入数据完毕,等待其他线程写入完毕
8   线程Thread-0写入数据完毕,等待其他线程写入完毕
9   Thread-0所有线程写入完毕,继续处理其他任务...
10  Thread-3所有线程写入完毕,继续处理其他任务...
11  Thread-1所有线程写入完毕,继续处理其他任务...
12  Thread-2所有线程写入完毕,继续处理其他任务...
13  CyclicBarrier重用
14  线程Thread-4正在写入数据...
15  线程Thread-5正在写入数据...
16  线程Thread-6正在写入数据...
17  线程Thread-7正在写入数据...
18  线程Thread-7写入数据完毕,等待其他线程写入完毕
19  线程Thread-5写入数据完毕,等待其他线程写入完毕
20  线程Thread-6写入数据完毕,等待其他线程写入完毕
21  线程Thread-4写入数据完毕,等待其他线程写入完毕
22  Thread-4所有线程写入完毕,继续处理其他任务...
23  Thread-5所有线程写入完毕,继续处理其他任务...
24  Thread-6所有线程写入完毕,继续处理其他任务...
25  Thread-7所有线程写入完毕,继续处理其他任务...

从执行结果可以看出,在初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用。而CountDownLatch无法进行重复使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值