CyclicBarrier的工作原理及其实例
CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题。
1.CyclicBarrier的工作原理
CyclicBarrier大致是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。首先,便是可重复利用,说明该类创建的对象可以复用;其次,屏障则体现了该类的原理:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。
这里介绍CyclicBarrier的两个构造函数:CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction) :前者只需要声明需要拦截的线程数即可,而后者还需要定义一个等待所有线程到达屏障优先执行的Runnable对象。
实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。
举例说明:如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。
- package concurrent;
- import java.util.concurrent.CyclicBarrier;
- import java.util.concurrent.*;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.*;
- public class CyclicBarrierDemo {
- private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
- //当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
- private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
- public void run()
- {
- System.out.println("寝室四兄弟一起出发去球场");
- }
- });
- private static class GoThread extends Thread{
- private final String name;
- public GoThread(String name)
- {
- this.name=name;
- }
- public void run()
- {
- System.out.println(name+"开始从宿舍出发");
- try {
- Thread.sleep(1000);
- cb.await();//拦截线程
- System.out.println(name+"从楼底下出发");
- Thread.sleep(1000);
- System.out.println(name+"到达操场");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- catch(BrokenBarrierException e)
- {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String[] str= {"李明","王强","刘凯","赵杰"};
- for(int i=0;i<4;i++)
- {
- threadPool.execute(new GoThread(str[i]));
- }
- try
- {
- Thread.sleep(4000);
- System.out.println("四个人一起到达球场,现在开始打球");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
运行程序,得到如下结果:
- 李明开始从宿舍出发
- 赵杰开始从宿舍出发
- 王强开始从宿舍出发
- 刘凯开始从宿舍出发
- 寝室四兄弟一起出发去球场
- 赵杰从楼底下出发
- 李明从楼底下出发
- 刘凯从楼底下出发
- 王强从楼底下出发
- 赵杰到达操场
- 王强到达操场
- 李明到达操场
- 刘凯到达操场
- 四个人一起到达球场,现在开始打球
以上便是CyclicBarrier使用实例,通过await()方法对线程的拦截,拦截数加1,当拦截数为初始的parties,首先执行了barrierAction,然后对拦截的线程队列依次进行获取锁释放锁。接下来,在这个例子上讲解CyclicBarrier对象的复用特性。
- package concurrent;
- import java.util.concurrent.CyclicBarrier;
- import java.util.concurrent.*;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.*;
- public class CyclicBarrierDemo {
- private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
- //当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
- private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
- public void run()
- {
- System.out.println("寝室四兄弟一起出发去球场");
- }
- });
- private static class GoThread extends Thread{
- private final String name;
- public GoThread(String name)
- {
- this.name=name;
- }
- public void run()
- {
- System.out.println(name+"开始从宿舍出发");
- try {
- Thread.sleep(1000);
- cb.await();//拦截线程
- System.out.println(name+"从楼底下出发");
- Thread.sleep(1000);
- System.out.println(name+"到达操场");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- catch(BrokenBarrierException e)
- {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String[] str= {"李明","王强","刘凯","赵杰"};
- String[] str1= {"王二","洪光","雷兵","赵三"};
- for(int i=0;i<4;i++)
- {
- threadPool.execute(new GoThread(str[i]));
- }
- try
- {
- Thread.sleep(4000);
- System.out.println("四个人一起到达球场,现在开始打球");
- System.out.println("现在对CyclicBarrier进行复用.....");
- System.out.println("又来了一拨人,看看愿不愿意一起打:");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- //进行复用:
- for(int i=0;i<4;i++)
- {
- threadPool.execute(new GoThread(str1[i]));
- }
- try
- {
- Thread.sleep(4000);
- System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
- //System.out.println("现在对CyclicBarrier进行复用");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
运行如下程序,得到:
- 王强开始从宿舍出发
- 赵杰开始从宿舍出发
- 李明开始从宿舍出发
- 刘凯开始从宿舍出发
- 寝室四兄弟一起出发去球场
- 王强从楼底下出发
- 李明从楼底下出发
- 刘凯从楼底下出发
- 赵杰从楼底下出发
- 王强到达操场
- 李明到达操场
- 赵杰到达操场
- 刘凯到达操场
- 四个人一起到达球场,现在开始打球
- 现在对CyclicBarrier进行复用.....
- 又来了一拨人,看看愿不愿意一起打:
- 王二开始从宿舍出发
- 雷兵开始从宿舍出发
- 洪光开始从宿舍出发
- 赵三开始从宿舍出发
- 寝室四兄弟一起出发去球场
- 洪光从楼底下出发
- 王二从楼底下出发
- 雷兵从楼底下出发
- 赵三从楼底下出发
- 雷兵到达操场
- 赵三到达操场
- 洪光到达操场
- 王二到达操场
- 四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
由上面实例可了解CyclicBarrier的工作原理以及复用的特性。
2.通过CountDownLatch实现CyclicBarrier
CountDownLatch通过将await()方法和countDown()方法在不同线程组分别调用,从而实现线程组间的线程等待,即一个线程组等待另一个线程组执行结束再执行。而CyclicBarrier类则是通过调用await()方法实现线程组内的线程等待,即达到需要拦截的线程数,被拦截的线程才会依次获取锁,释放锁。那么将CountDownLatch的用法进行转换,即在同一个线程组内调用await()方法和countDown()方法,则可实现CyclicBarrier类的功能。但是注意的是必须先调用countDown()方法,才能调用await()方法,因为一旦调用await()方法,该线程后面的内容便不再执行,那么count值无法改变。具体代码如下:
- package concurrent;
- import java.util.Vector;
- import java.util.concurrent.BrokenBarrierException;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- public class CyclicBarrierWithCount {
- private final static CountDownLatch cdl=new CountDownLatch(3);
- private final static ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//使用线程池
- private static class GoThread extends Thread{
- private final String name;
- public GoThread(String name)
- {
- this.name=name;
- }
- public void run()
- {
- System.out.println(name+"开始从宿舍出发");
- cdl.countDown();
- try
- {
- Thread.sleep(1000);
- cdl.await();//拦截线程
- System.out.println(name+"从楼底下出发");
- Thread.sleep(1000);
- System.out.println(name+"到达操场");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String[] str= {"李明","王强","刘凯","赵杰"};
- String[] str1= {"王二","洪光","雷兵","赵三"};
- for(int i=0;i<4;i++)
- {
- threadPool.execute(new GoThread(str[i]));
- }
- try
- {
- Thread.sleep(4000);
- System.out.println("四个人一起到达球场,现在开始打球");
- System.out.println("现在对CyclicBarrier进行复用.....");
- System.out.println("又来了一拨人,看看愿不愿意一起打:");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- for(int i=0;i<4;i++)
- {
- threadPool.execute(new GoThread(str1[i]));
- }
- try
- {
- Thread.sleep(4000);
- System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
- //System.out.println("现在对CyclicBarrier进行复用");
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
执行上述代码,结果如下:
- 李明开始从宿舍出发
- 赵杰开始从宿舍出发
- 王强开始从宿舍出发
- 刘凯开始从宿舍出发
- 王强从楼底下出发
- 刘凯从楼底下出发
- 李明从楼底下出发
- 赵杰从楼底下出发
- 李明到达操场
- 赵杰到达操场
- 王强到达操场
- 刘凯到达操场
- 四个人一起到达球场,现在开始打球
- 现在对CyclicBarrier进行复用.....
- 又来了一拨人,看看愿不愿意一起打:
- 王二开始从宿舍出发
- 洪光开始从宿舍出发
- 雷兵开始从宿舍出发
- 赵三开始从宿舍出发
- 王二从楼底下出发
- 洪光从楼底下出发
- 雷兵从楼底下出发
- 赵三从楼底下出发
- 洪光到达操场
- 王二到达操场
- 雷兵到达操场
- 赵三到达操场
- 四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
由上面可知,CountDownLatch一定情况下可以实现CyclicBarrier类的功能。
3.CountDownLatch和CyclicBarrier的比较
1.CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。
2.CountDownLatch是减计数方式,而CyclicBarrier是加计数方式。
3.CountDownLatch计数为0无法重置,而CyclicBarrier计数达到初始值,则可以重置。
4.CountDownLatch不可以复用,而CyclicBarrier可以复用。
CyclicBarrier的工作原理及其实例的更多相关文章
- Webservice工作原理及实例
Web Service工作原理及实例 一.Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者In ...
- Ajax工作原理及实例
1.关于ajax的名字 ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式. 2. ...
- WebService 工作原理及实例教程
一.WebService到底是什么? 先来看下标准的定义:Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统 ...
- Java并发包中CyclicBarrier的工作原理、使用示例
1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ...
- [转载] Web Service工作原理及实例
一.Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求, ...
- 详解AJAX工作原理以及实例讲解(通俗易懂)
什么是 AJAX ? AJAX = 异步 JavaScript 和 XML. AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味 ...
- tcp/iP协议族——IP工作原理及实例具体解释(下)
IP协议具体解释 上一篇文章文章主要介绍了IP服务的特点,IPv4头部结构IP分片.并用tcpdump抓取数据包,来观察IP数据报传送过程中IP的格式,以及分片的过程.本文主要介绍IP路由,IP ...
- TCP/IP协议族——IP工作原理及实例具体解释(上)
IP协议具体解释 本文主要介绍了IP服务特点,头部结构,IP分片知识,并用tcpdump抓取数据包.来观察IP数据报传送过程中IP的格式,以及分片的过程. IP头部信息:IP头部信息出如今每一个 ...
- ☕【Java深层系列】「并发编程系列」让我们一起探索一下CyclicBarrier的技术原理和源码分析
CyclicBarrier和CountDownLatch CyclicBarrier和CountDownLatch 都位于java.util.concurrent这个包下,其工作原理的核心要点: Cy ...
随机推荐
- 博客 | 基于Travis CI实现Hexo在Github和Coding的同步自动化部署
文章目录 完成Hexo主题安装和配置 基于Travis CI实现同步部署 参考内容 相关链接 待补充 完成Hexo主题安装和配置 如果您还没有安装Hexo环境,请参考Hexo文档安装,也给出这样两篇博 ...
- [BZOJ1194][HNOI2006][强连通分量Tarjan+dfs]潘多拉的盒子
[BZOJ1194][HNOI2006]潘多拉的盒子 Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语 ...
- 在java代码中设置margin
我们平常可以直接在xml里设置margin,如: <ImageView android:layout_margin="5dip" android:src="@dra ...
- bin/...的访问被拒绝被拒绝的问题
复制到bin.... 对路径bin/.... 的访问被拒绝出现这们的问题,把源码管理器中项目的Bin目录删除,重新获取就要以了
- UVa 816 (BFS求最短路)
/*816 - Abbott's Revenge ---代码完全参考刘汝佳算法入门经典 ---strchr() 用来查找某字符在字符串中首次出现的位置,其原型为:char * strchr (cons ...
- ol 接入百度地图
ol5 如何接入百度地图,网上的资料很多,但是大多都有问题,在级别放大时,地图发生扭曲.为此注重研究了下ol5 接入百度地图的方法. 首先明确以下问题: 百度地图的投影是3857. 百度地图的分辨率和 ...
- JWT笔记(2)
上文对JWT模块进行了一个简单的分析.这篇文章稍微做出一些深入的了解. 一,Header篡改攻击 因为JWT的Header是强制有效并且是明文传输(Base64URL编码,几乎等同于明文).那么恶意用 ...
- 不厚道一回->Omnifocus 2 for mac license
rt, 发个Omnifocus 2 for mac license. 其实Omnifocus 2的价格已经还算亲民了..可惜手贱一下子就找到了,所以没买了..不敢独享,所以分享给需要的人..有能力还是 ...
- Codeforces #263 div2 解题报告
比赛链接:http://codeforces.com/contest/462 这次比赛的时候,刚刚注冊的时候非常想好好的做一下,可是网上喝了个小酒之后.也就迷迷糊糊地看了题目,做了几题.一觉醒来发现r ...
- bat 处理文件路径 (所在目录,文件名,文件后缀名,完整文件名,无后缀路径)
echo off setlocal enabledelayedexpansion set "EXCEL_DIR=%cd%\excel" for /R %EXCEL_DIR% %%f ...