戏说java多线程之CyclicBarrier(循环栅栏)的CyclicBarrier(int parties)构造方法
CyclicBarrier是JDK 1.5 concurrent包出现的一个用于解决多条线程阻塞,当达到一定条件时一起放行的一个类。我们先来看这样一个简单的需求。
现在我有一个写入数据的类,继承Runable接口:
public class WriteDateThread implements Runnable { @Override
public void run() { System.out.println(Thread.currentThread().getName() + "开始写入数据..."); System.out.println(Thread.currentThread().getName() + "写入数据完毕!");
}
}
代码很简单,只有两个输出语句,一个是开始写入数据,然后打印写入完毕。
然后创建这样一个Main函数的类:
public class CyclicBarrierMain {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
WriteDateThread thread = new WriteDateThread();
Thread t = new Thread(thread);
t.start();
}
}
}
同样,也是很简单的代码,我现在通过main函数来启动这样5个WriteDateThread线程,想必大家应该清楚线程之间的执行顺序在于谁抢到了CPU执行权,于是执行结果如下:
我们可看到,每条线程的开始执行和执行完毕和其他线程都是交错的,这也符合了多线程的执行规律。我们来想象一个实际中的需求,现在有5个玩家同时进入一场游戏对决当中,系统需要读取玩家的数据,当读取完成后,允许玩家进入游戏中。按理来说,当系统读取完某玩家的数据后,该玩家就拥有入场权限了,如果我们不加任何干预的话,会发生什么情况呢?正如上图中执行结果那样,系统读取0,2号玩家的数据,然后2号玩家读取完毕,于是2号玩家先进场,然后再读取4,1号玩家的数据...以此类推。我想大家肯定玩过LOL或者农药这类的多人竞技游戏,实际情况是这样的吗?不是!实际情况是,系统加载完某玩家的数据后,会让该玩家等待其他玩家的数据加载,当所有玩家的数据都加载完毕后,大家同时进入游戏(这里不考虑掉线等意外情况)。
好了,有了这样一个实际案例,我说出现在的需求大家就很容易理解了,现在的需求是要求所有线程在执行完写入数据后,不能在向下执行,而是等待所有其他线程执行完写入数据,然后大家再一起执行“写入数据完毕...”,我们当然可以用java.util.concurrent包下CountDownLatch计数器来自行控制,但是这里我希望用更智能化一些的CyclicBarrier(循环栅栏)来完成这个需求:
代码如下:
public class WriteDateThread implements Runnable { private CyclicBarrier barrier; public WriteDateThread(CyclicBarrier barrier) {
this.barrier = barrier;
} @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始写入数据...");
try {
barrier.await(); //通过调用await方法在此处设置栅栏,使得线程执行到此进入阻塞状态等待其他线程执行完成。
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "写入数据完毕!");
}
}
public class CyclicBarrierMain {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
WriteDateThread thread = new WriteDateThread(barrier);
Thread t = new Thread(thread);
t.start();
}
}
}
我们通过调用CyclicBarrier的await方法,使线程执行到此的时候,进入到阻塞状态,并且等待其他线程都执行到此后,再统一放行,让它们同时向下继续执行(放行后具体谁先执行,还是要看谁先抢到CPU执行权)
运行结果:
如此,便得到了我们需求中想要的结果了。
CyclicBarrier的用法到此就介绍完毕,下面我着重介绍一下,CyclicBarrier最常用的一个构造函数,也是我第一次使用时非常令我困惑的一个点,就是我上面代码CyclicBarrierMain中第3行CyclicBarrier barrier = new CyclicBarrier(int parties);这个构造器。
我们现在有5条线程,因此我传入5这毫无疑问,但是这个5究竟是什么意思呢?如果传入的值大于5或者小于5又会是什么结果呢?大家不妨可以自行试验一下,只要我们传入的不是5,程序都会一直处于阻塞状态,停不下来。
我翻看过别人别这个值的解释,比较抽象,原文如下:
参数parties指定线程数量,当指定的线程值都到达栅栏点时,栅栏打开,线程恢复。需要注意的是,当指定的线程数量大于启动的线程数量,比如修改上例中的代码,只启动4个线程,那么所有的线程将执行到await处然后一直处于等待状态。第二种情况是指定的线程数量小于启动的线程,上例代码,启动6个线程,那么当第5个线程到达栅栏点时,那么这5个线程就会恢复继续执行,而第6个线程将一直处于阻塞状态。
我将通过一个小故事来说明这个问题。
故事是这样的,有这样一场骑马比赛,有5名骑手,他们各自拥有自己的赛道,并且每条赛道上在开赛之前就已经预设好了一个栅栏。另外还有一名栅栏管理员和一名指挥官,指挥官只负责向管理员发号施令,他只会告诉管理员一个数字n。这个数字的意思是,管理员要打开的栅栏的数量,前提是他必须看到有n个骑手都到达栅栏前,才会将这n个骑手面前的栅栏打开。于是管理会记着这个数字n在栅栏旁守着,当有1名、2名...骑手到达栅栏时,管理员不予理会,直到有第n名骑手到达栅栏处时,管理员统一打开栅栏放行,骑手们得以同时继续骑行,而管理员则完成了任务,进屋休息去了。
如果指挥官告诉管理员的数字是5,那么一切和平的进行,不会发生任何问题。但是如果指挥官,告诉管理员的数字不是5,比如说4或者6。这样问题就产生了,对于这两种情况,我们分别来分析一下:
- 指挥官给出的数字大于骑手数量,这里我们假设是6
我前边说过,每条赛道上在开赛之前都预设好了栅栏,那么此时管理从指挥官那里得到的数字是6,因此管理员要执行的任务就是看着栅栏,直到第1名骑手到达,第2名骑手到达,第3名...直到管理员看见第6名骑手到达时才进行统一放行,但是问题是,总共的参赛选手只有5名啊!这下好了,管理员是个死心眼,他不看见第6名骑手他是绝对不会进行放行的,那么大家可以想象一下,5名骑手被卡在栅栏前不能继续后面的比赛,而管理员在那里痴痴的等着第6名骑手的到来,就这样一直到天荒地老...
- 指挥官给出的数字小于骑手数量,这里我们假设是4
还像刚才一样,管理拿到数字4后便在栅栏旁等着,直到看见骑手的相继到来,第1名,第2名,第3名,第4名!管理员看见第4名骑手到来后,立即打开他们各自赛道前的栅栏,于是这4名骑手继续后面的比赛,管理员也完成了自己的任务,进屋睡觉去了...等等!还有第5名骑手呢!第5名骑手到达栅栏前惊奇的发现,其他赛道都被放行了,但是他的赛道上还有栅栏,他不得不等下等着管理员放行,但是郁闷的是,他并不知道管理员再也不会回来了,于是他就这样孤独的一直等待着...
好了,故事讲完了,现在我们通过这个故事映射到代码当中,骑手其实就是我们开的线程,他们各自赛道上赛前设置的栅栏,其实就是我们在程序执行前调用的CyclicBarrier的await方法,程序执行到这个栅栏处会被阻塞住。指挥官其实就是我们自己(写程序的人),管理员就是CyclicBarrier内部实现唤醒线程的解决方案,我们(指挥官)给管理员发号的施令(一个数字)就是我们通过CyclicBarrier(int parties)传入的数值。
如果我们传入的是6,因为我们只有5条线程,第6条线程永远不会到来,因此管理员也就永远不会放行栅栏,所有线程将会阻塞在栅栏出等待那不存在的第6条线程,我们更改代码中传入的数值为6,得到的运行结果:
可以看到,5条线程全部阻塞到了调用await方法的地方,并且程序一直处于阻塞状态。
如果我们传入的是4,管理员在看到第4条线程的到来时,就会放行他们4个面前的栅栏,于是这4条线程继续执行,此时管理员完成任务回家睡觉了,第5条线程到来时,会一直卡在栅栏前。我们更改代码中传入的数值为4,得到的运行结果:
通过上述的通俗的讲解,相信大家不会在觉得CyclicBarrier的(int parties)构造方法那么晦涩难懂了,由于本人所知有限,故难保证文中有什么错误之处,欢迎大牛不惜吝啬下方留言指正。
戏说java多线程之CyclicBarrier(循环栅栏)的CyclicBarrier(int parties)构造方法的更多相关文章
- java并发之(4):Semaphore信号量、CounDownLatch计数锁存器和CyclicBarrier循环栅栏
简介 java.util.concurrent包是Java 5的一个重大改进,java.util.concurrent包提供了多种线程间同步和通信的机制,比如Executors, Queues, Ti ...
- Java多线程之ConcurrentSkipListMap深入分析(转)
Java多线程之ConcurrentSkipListMap深入分析 一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...
- JAVA多线程之wait/notify
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...
- JAVA多线程之volatile 与 synchronized 的比较
一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...
- Java多线程之Runnable与Thread
Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...
- java多线程之yield,join,wait,sleep的区别
Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...
- JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...
- java多线程之wait和notify协作,生产者和消费者
这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...
- 同步机制之--java CyclicBarrier 循环栅栏
CyclicBarrier介绍一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待 ...
随机推荐
- egametang框架服务端运行流程
et框架的构建块主要由entity和componet组成,类似unity的组件.一个Entity可以挂载多个不同Component.Entity和Component的共同基类Disposer用于提供对 ...
- ubuntu下安装memcached与php扩展测试使用
1,memcached需要libevent,所以要先安装它 下载地址:http://download.chinaunix.net/download.php?id=45065&ResourceI ...
- Apollo阿波罗配置中心docker
前言 在分布式系统中,要改个配置涉及到很多个系统,一个一个改效率低下,吃力不讨好.用配置中心可以解决这个问题.当然配置中心有不少,以下对比的表格是照搬Apollo Wiki的. 功能点 Apollo ...
- spring-boot-starter家族成员简介
应用程序starters 以下应用程序starters是Spring Boot在org.springframework.boot组下提供的: springboot使用指南https://docs.sp ...
- 阿里云ECS重置磁盘到SSH登录
1.登录阿里云(www.aliyun.com) -- > 控制台: 2.点击左边的"云服务器ECS": 3.点击上面"第二步",进入页面之后,点击&quo ...
- 使用stringstream对象简化类型转换
< sstream>库定义了三种类:istringstream.ostringstream和stringstream,分别用来进行流的输入.输出和输入输出操作.另外,每个类都有一个对应的宽 ...
- 蓝桥杯 求最大值 dp
这题很暴力的一个DP,d[i][j]表示前i个数对选择一些Ai的和为j的最大Bi和. 状态转移方程: dp[i][j]=max(dp[i][j],dp[i-1][j-sc[i].a]+sc[i].b) ...
- UVA-714 二分
把可能的进行二分判断,判断的时候尽量向右取,一直取到不能去为止,这样才有可能成功分割. 判断是否可以把up作为最大值的代码: bool judge(LL up){ if(up < Big) re ...
- Spring Data JPA 入门Demo
什么是JPA呢? 其实JPA可以说是一种规范,是java5.0之后提出来的用于持久化的一套规范:它不是任何一种ORM框架,在我看来,是现有ORM框架在这个规范下去实现持久层. 它的出现是为了简化现有的 ...
- UVA - 12186 Another Crisis (树形DP)
思路:dp[i]表示让上司i签字至少需要多少工人签字. 转移方程:将i的所有节点根据所需工人数量升序排序,设i需要k个下属签字,dp[i] = sum{dp[v]| 0 <= v & ...