CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题。

1.CyclicBarrier的工作原理

CyclicBarrier大致是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。首先,便是可重复利用,说明该类创建的对象可以复用;其次,屏障则体现了该类的原理:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。

这里介绍CyclicBarrier的两个构造函数:CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction) :前者只需要声明需要拦截的线程数即可,而后者还需要定义一个等待所有线程到达屏障优先执行的Runnable对象。

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

举例说明:如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。

  1.  
    package concurrent;
  2.  
    import java.util.concurrent.CyclicBarrier;
  3.  
    import java.util.concurrent.*;
  4.  
    import java.util.concurrent.LinkedBlockingQueue;
  5.  
    import java.util.concurrent.ThreadPoolExecutor;
  6.  
    import java.util.concurrent.TimeUnit;
  7.  
    import java.util.*;
  8.  
    public class CyclicBarrierDemo {
  9.  
    private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
  10.  
    //当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
  11.  
    private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
  12.  
    public void run()
  13.  
    {
  14.  
    System.out.println("寝室四兄弟一起出发去球场");
  15.  
    }
  16.  
    });
  17.  
    private static class GoThread extends Thread{
  18.  
    private final String name;
  19.  
    public GoThread(String name)
  20.  
    {
  21.  
    this.name=name;
  22.  
    }
  23.  
    public void run()
  24.  
    {
  25.  
    System.out.println(name+"开始从宿舍出发");
  26.  
    try {
  27.  
    Thread.sleep(1000);
  28.  
    cb.await();//拦截线程
  29.  
    System.out.println(name+"从楼底下出发");
  30.  
    Thread.sleep(1000);
  31.  
    System.out.println(name+"到达操场");
  32.  
     
  33.  
    }
  34.  
    catch(InterruptedException e)
  35.  
    {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    catch(BrokenBarrierException e)
  39.  
    {
  40.  
    e.printStackTrace();
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
    String[] str= {"李明","王强","刘凯","赵杰"};
  47.  
    for(int i=0;i<4;i++)
  48.  
    {
  49.  
    threadPool.execute(new GoThread(str[i]));
  50.  
    }
  51.  
    try
  52.  
    {
  53.  
    Thread.sleep(4000);
  54.  
    System.out.println("四个人一起到达球场,现在开始打球");
  55.  
    }
  56.  
    catch(InterruptedException e)
  57.  
    {
  58.  
    e.printStackTrace();
  59.  
    }
  60.  
     
  61.  
     
  62.  
    }
  63.  
     
  64.  
    }

运行程序,得到如下结果:

  1.  
    李明开始从宿舍出发
  2.  
    赵杰开始从宿舍出发
  3.  
    王强开始从宿舍出发
  4.  
    刘凯开始从宿舍出发
  5.  
    寝室四兄弟一起出发去球场
  6.  
    赵杰从楼底下出发
  7.  
    李明从楼底下出发
  8.  
    刘凯从楼底下出发
  9.  
    王强从楼底下出发
  10.  
    赵杰到达操场
  11.  
    王强到达操场
  12.  
    李明到达操场
  13.  
    刘凯到达操场
  14.  
    四个人一起到达球场,现在开始打球

以上便是CyclicBarrier使用实例,通过await()方法对线程的拦截,拦截数加1,当拦截数为初始的parties,首先执行了barrierAction,然后对拦截的线程队列依次进行获取锁释放锁。接下来,在这个例子上讲解CyclicBarrier对象的复用特性。

  1.  
    package concurrent;
  2.  
    import java.util.concurrent.CyclicBarrier;
  3.  
    import java.util.concurrent.*;
  4.  
    import java.util.concurrent.LinkedBlockingQueue;
  5.  
    import java.util.concurrent.ThreadPoolExecutor;
  6.  
    import java.util.concurrent.TimeUnit;
  7.  
    import java.util.*;
  8.  
    public class CyclicBarrierDemo {
  9.  
    private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
  10.  
    //当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
  11.  
    private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
  12.  
    public void run()
  13.  
    {
  14.  
    System.out.println("寝室四兄弟一起出发去球场");
  15.  
    }
  16.  
    });
  17.  
    private static class GoThread extends Thread{
  18.  
    private final String name;
  19.  
    public GoThread(String name)
  20.  
    {
  21.  
    this.name=name;
  22.  
    }
  23.  
    public void run()
  24.  
    {
  25.  
    System.out.println(name+"开始从宿舍出发");
  26.  
    try {
  27.  
    Thread.sleep(1000);
  28.  
    cb.await();//拦截线程
  29.  
    System.out.println(name+"从楼底下出发");
  30.  
    Thread.sleep(1000);
  31.  
    System.out.println(name+"到达操场");
  32.  
     
  33.  
    }
  34.  
    catch(InterruptedException e)
  35.  
    {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    catch(BrokenBarrierException e)
  39.  
    {
  40.  
    e.printStackTrace();
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
    String[] str= {"李明","王强","刘凯","赵杰"};
  47.  
    String[] str1= {"王二","洪光","雷兵","赵三"};
  48.  
    for(int i=0;i<4;i++)
  49.  
    {
  50.  
    threadPool.execute(new GoThread(str[i]));
  51.  
    }
  52.  
    try
  53.  
    {
  54.  
    Thread.sleep(4000);
  55.  
    System.out.println("四个人一起到达球场,现在开始打球");
  56.  
    System.out.println("现在对CyclicBarrier进行复用.....");
  57.  
    System.out.println("又来了一拨人,看看愿不愿意一起打:");
  58.  
    }
  59.  
    catch(InterruptedException e)
  60.  
    {
  61.  
    e.printStackTrace();
  62.  
    }
  63.  
    //进行复用:
  64.  
    for(int i=0;i<4;i++)
  65.  
    {
  66.  
    threadPool.execute(new GoThread(str1[i]));
  67.  
    }
  68.  
    try
  69.  
    {
  70.  
    Thread.sleep(4000);
  71.  
    System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
  72.  
    //System.out.println("现在对CyclicBarrier进行复用");
  73.  
    }
  74.  
    catch(InterruptedException e)
  75.  
    {
  76.  
    e.printStackTrace();
  77.  
    }
  78.  
     
  79.  
     
  80.  
     
  81.  
    }
  82.  
     
  83.  
    }

运行如下程序,得到:

  1.  
    王强开始从宿舍出发
  2.  
    赵杰开始从宿舍出发
  3.  
    李明开始从宿舍出发
  4.  
    刘凯开始从宿舍出发
  5.  
    寝室四兄弟一起出发去球场
  6.  
    王强从楼底下出发
  7.  
    李明从楼底下出发
  8.  
    刘凯从楼底下出发
  9.  
    赵杰从楼底下出发
  10.  
    王强到达操场
  11.  
    李明到达操场
  12.  
    赵杰到达操场
  13.  
    刘凯到达操场
  14.  
    四个人一起到达球场,现在开始打球
  15.  
    现在对CyclicBarrier进行复用.....
  16.  
    又来了一拨人,看看愿不愿意一起打:
  17.  
    王二开始从宿舍出发
  18.  
    雷兵开始从宿舍出发
  19.  
    洪光开始从宿舍出发
  20.  
    赵三开始从宿舍出发
  21.  
    寝室四兄弟一起出发去球场
  22.  
    洪光从楼底下出发
  23.  
    王二从楼底下出发
  24.  
    雷兵从楼底下出发
  25.  
    赵三从楼底下出发
  26.  
    雷兵到达操场
  27.  
    赵三到达操场
  28.  
    洪光到达操场
  29.  
    王二到达操场
  30.  
    四个人一起到达球场,表示愿意一起打球,现在八个人开始打球

由上面实例可了解CyclicBarrier的工作原理以及复用的特性。

2.通过CountDownLatch实现CyclicBarrier

CountDownLatch通过将await()方法和countDown()方法在不同线程组分别调用,从而实现线程组间的线程等待,即一个线程组等待另一个线程组执行结束再执行。而CyclicBarrier类则是通过调用await()方法实现线程组内的线程等待,即达到需要拦截的线程数,被拦截的线程才会依次获取锁,释放锁。那么将CountDownLatch的用法进行转换,即在同一个线程组内调用await()方法和countDown()方法,则可实现CyclicBarrier类的功能。但是注意的是必须先调用countDown()方法,才能调用await()方法,因为一旦调用await()方法,该线程后面的内容便不再执行,那么count值无法改变。具体代码如下:

  1.  
    package concurrent;
  2.  
     
  3.  
    import java.util.Vector;
  4.  
    import java.util.concurrent.BrokenBarrierException;
  5.  
    import java.util.concurrent.CountDownLatch;
  6.  
    import java.util.concurrent.LinkedBlockingQueue;
  7.  
    import java.util.concurrent.ThreadPoolExecutor;
  8.  
    import java.util.concurrent.TimeUnit;
  9.  
    public class CyclicBarrierWithCount {
  10.  
    private final static CountDownLatch cdl=new CountDownLatch(3);
  11.  
    private final static ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//使用线程池
  12.  
     
  13.  
    private static class GoThread extends Thread{
  14.  
    private final String name;
  15.  
     
  16.  
    public GoThread(String name)
  17.  
    {
  18.  
    this.name=name;
  19.  
     
  20.  
    }
  21.  
    public void run()
  22.  
    {
  23.  
    System.out.println(name+"开始从宿舍出发");
  24.  
    cdl.countDown();
  25.  
    try
  26.  
    {
  27.  
    Thread.sleep(1000);
  28.  
    cdl.await();//拦截线程
  29.  
    System.out.println(name+"从楼底下出发");
  30.  
    Thread.sleep(1000);
  31.  
    System.out.println(name+"到达操场");
  32.  
    }
  33.  
    catch(InterruptedException e)
  34.  
    {
  35.  
    e.printStackTrace();
  36.  
    }
  37.  
     
  38.  
     
  39.  
     
  40.  
    }
  41.  
    }
  42.  
     
  43.  
     
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
     
  47.  
     
  48.  
    String[] str= {"李明","王强","刘凯","赵杰"};
  49.  
    String[] str1= {"王二","洪光","雷兵","赵三"};
  50.  
    for(int i=0;i<4;i++)
  51.  
    {
  52.  
    threadPool.execute(new GoThread(str[i]));
  53.  
    }
  54.  
    try
  55.  
    {
  56.  
    Thread.sleep(4000);
  57.  
    System.out.println("四个人一起到达球场,现在开始打球");
  58.  
    System.out.println("现在对CyclicBarrier进行复用.....");
  59.  
    System.out.println("又来了一拨人,看看愿不愿意一起打:");
  60.  
    }
  61.  
    catch(InterruptedException e)
  62.  
    {
  63.  
    e.printStackTrace();
  64.  
    }
  65.  
    for(int i=0;i<4;i++)
  66.  
    {
  67.  
    threadPool.execute(new GoThread(str1[i]));
  68.  
    }
  69.  
    try
  70.  
    {
  71.  
    Thread.sleep(4000);
  72.  
    System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
  73.  
    //System.out.println("现在对CyclicBarrier进行复用");
  74.  
    }
  75.  
    catch(InterruptedException e)
  76.  
    {
  77.  
    e.printStackTrace();
  78.  
    }
  79.  
    }
  80.  
     
  81.  
    }

执行上述代码,结果如下:

  1.  
    李明开始从宿舍出发
  2.  
    赵杰开始从宿舍出发
  3.  
    王强开始从宿舍出发
  4.  
    刘凯开始从宿舍出发
  5.  
    王强从楼底下出发
  6.  
    刘凯从楼底下出发
  7.  
    李明从楼底下出发
  8.  
    赵杰从楼底下出发
  9.  
    李明到达操场
  10.  
    赵杰到达操场
  11.  
    王强到达操场
  12.  
    刘凯到达操场
  13.  
    四个人一起到达球场,现在开始打球
  14.  
    现在对CyclicBarrier进行复用.....
  15.  
    又来了一拨人,看看愿不愿意一起打:
  16.  
    王二开始从宿舍出发
  17.  
    洪光开始从宿舍出发
  18.  
    雷兵开始从宿舍出发
  19.  
    赵三开始从宿舍出发
  20.  
    王二从楼底下出发
  21.  
    洪光从楼底下出发
  22.  
    雷兵从楼底下出发
  23.  
    赵三从楼底下出发
  24.  
    洪光到达操场
  25.  
    王二到达操场
  26.  
    雷兵到达操场
  27.  
    赵三到达操场
  28.  
    四个人一起到达球场,表示愿意一起打球,现在八个人开始打球

由上面可知,CountDownLatch一定情况下可以实现CyclicBarrier类的功能。

3.CountDownLatch和CyclicBarrier的比较

1.CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。

2.CountDownLatch是减计数方式,而CyclicBarrier是加计数方式。

3.CountDownLatch计数为0无法重置,而CyclicBarrier计数达到初始值,则可以重置。

4.CountDownLatch不可以复用,而CyclicBarrier可以复用。

CyclicBarrier的工作原理及其实例的更多相关文章

  1. Webservice工作原理及实例

    Web Service工作原理及实例   一.Web Service基本概念   Web Service也叫XML Web Service WebService是一种可以接收从Internet或者In ...

  2. Ajax工作原理及实例

    1.关于ajax的名字 ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式. 2. ...

  3. WebService 工作原理及实例教程

    一.WebService到底是什么? 先来看下标准的定义:Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统 ...

  4. Java并发包中CyclicBarrier的工作原理、使用示例

    1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ...

  5. [转载] Web Service工作原理及实例

    一.Web Service基本概念   Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求, ...

  6. 详解AJAX工作原理以及实例讲解(通俗易懂)

    什么是 AJAX ? AJAX = 异步 JavaScript 和 XML. AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味 ...

  7. tcp/iP协议族——IP工作原理及实例具体解释(下)

     IP协议具体解释 上一篇文章文章主要介绍了IP服务的特点,IPv4头部结构IP分片.并用tcpdump抓取数据包,来观察IP数据报传送过程中IP的格式,以及分片的过程.本文主要介绍IP路由,IP ...

  8. TCP/IP协议族——IP工作原理及实例具体解释(上)

     IP协议具体解释 本文主要介绍了IP服务特点,头部结构,IP分片知识,并用tcpdump抓取数据包.来观察IP数据报传送过程中IP的格式,以及分片的过程. IP头部信息:IP头部信息出如今每一个 ...

  9. ☕【Java深层系列】「并发编程系列」让我们一起探索一下CyclicBarrier的技术原理和源码分析

    CyclicBarrier和CountDownLatch CyclicBarrier和CountDownLatch 都位于java.util.concurrent这个包下,其工作原理的核心要点: Cy ...

随机推荐

  1. mysql----kill慢查询

    每个与mysqld的连接都在一个独立的线程里运行,您可以使用SHOW PROCESSLIST语句查看哪些线程正在运行,并使用KILL thread_id语句终止一个线程. 如果您拥有SUPER权限,您 ...

  2. CentOS 6与CentOS 7的区别收集

    说明: 1.CentOS与Ubuntu没有什么可比性,底层都是Linux,并且Ubuntu在YY广泛的使用,这些并不能说明那个强大哪个不行,只要能解决问题的都是好家伙. 2.市面上教程基本都是基于6, ...

  3. ESB 12种跑法

    ESB 12种跑法 请求响应: MQ-MQ             MQ-Webservice                Webservice-MQ     Webservice-Webservi ...

  4. 转: MyEclipse 10.0,9.0,8.0 下添加jadClipse反编译插件

    MyEclipse 10.0,9.0,8.0 下添加jadClipse反编译插件 (2012-11-19 15:36:35) 转载▼ 标签: myeclipse jad 反编译 插件 it 分类: M ...

  5. 使用WIFI连接android进行调试和adb操作

    本人需要wifi连接android进行调试的原因主要是要经常用到IDA pro进行调试,但手头有的IDA Pro版本只是windows的,开发可能更多用Mac OS X了,来回拔插.调试很不方便,所以 ...

  6. 字体和颜色 Font Color 基础笔记

    private void SelectFontAndColor_Load(object sender, EventArgs e) {//窗体加载的时候,初始化字体 //返回所有的字体 FontFami ...

  7. Android学习(十三) BroadcastReceiver组件(广播)

    一.Broadcast(广播) 是一种广泛应用在应用程序之间传输信息的机制. 二.Broadcast(广播接收器) 是对发送出来的广播进行过滤接收并响应的一类组件,它就是用来接收来自系统和应用中的广播 ...

  8. C3:建造者模式 Builder

    将一个复杂对象的创建与表示分离,使得同样的构建过程可以创建不同的表示. 应用场景: A.创建这个对象通常需要较多的参数,才能完整的表示该对象.B.类的各个组成部分的具体实现类或算法经常面临变化,但将他 ...

  9. Vue-router(vue2.0)用法示例

    一.新建3个组件 1./src/components/post.vue <template> <div> hello world! this is POST! </div ...

  10. Linux 各个子系统以及监控、测试、优化这些子系统所用到的工具

    监控 测试: 3.优化