1.引子
天气不错,外面阳光很好!我们接着并发编程系列的上一篇(并发流程控制之CountDownLatch),通过倒计时门栓,我们模拟运动员赛跑,实现了一等多、多等一的编程模型。这里你还记得CountDownLatch的特点吗?它只能倒数计数使用一次,是一个一次性门栓。
今天我要分享给你的是CyclicBarrier,它是循环栅栏,使用方式与CountDownLatch有点像。不同的是它主要的应用场景,适合于我们生活中拼团、凑团模型,比如电商购物拼团,比如参加旅游公司拼团等。CyclicBarrier另外一个区别于CountDownLatch的地方是,它可以重复循环使用,这也是为什么我们通常把CyclicBarrier叫做循环栅栏,而把CountDownLatch叫做一次性门栓的原因。
话不多说,还是让我们show code吧!
2.案例
2.1.代码演示
案例描述:
- 通过案例,模拟旅游公司拼团旅游
- 假设某旅游公司,推出了一套x地xx天 的经济实惠的拼团家庭旅游套餐产品,限定拼团3人(满3人即可成为一团)
- 因为产品经济实惠,天气还不错,非常受欢迎,一下子来了8个人准备参团旅游
- 你是这款产品的开发工程师
- 你该如何实现呢?
/**
* 线程协作之CyclicBarrier
*
* @author ThinkPad
* @version 1.0
* @date 2021/1/31 9:19
*/
public class CyclicBarrierDemo {
/**
* 旅游公司规定的拼团规模,3人一个团(只要凑足了3个人即可出发旅游)
*/
public static CyclicBarrier tripCyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("-----------------本次参团人员满,可以出发了.H起来吧-----------------");
}
});
public static void main(String[] args) {
// 拼团旅游,今天天气不错,来的人很多(来了8个人)
for(int i = 1; i <= 8; i++){
new Thread(new TripTask(), "消费者(" + i +")").start();
}
}
/**
* 旅游公司拼团旅游
*/
static class TripTask implements Runnable{
@Override
public void run() {
// 参团人员名称
String name = Thread.currentThread().getName();
System.out.println(name + ".计划报名参团旅游.正前往旅游公司.");
try{
// 随机休眠,模拟去往旅游公司路上消耗的时间
Random random = new Random();
long time = random.nextInt(1000);
Thread.sleep(time);
// 等候其他参团人员
System.out.println(name + ".到达旅游公司.耗时【" + time + "】毫秒.开始等待其他参团人员.");
tripCyclicBarrier.await();
// 拼团人员到齐,出发旅游
System.out.println(name + ".参加的旅游团.人员到齐.开开心心去旅游喽.");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
执行结果:
消费者(3).计划报名参团旅游.正前往旅游公司.
消费者(4).计划报名参团旅游.正前往旅游公司.
消费者(7).计划报名参团旅游.正前往旅游公司.
消费者(1).计划报名参团旅游.正前往旅游公司.
消费者(8).计划报名参团旅游.正前往旅游公司.
消费者(2).计划报名参团旅游.正前往旅游公司.
消费者(5).计划报名参团旅游.正前往旅游公司.
消费者(6).计划报名参团旅游.正前往旅游公司.
消费者(3).到达旅游公司.耗时【107】毫秒.开始等待其他参团人员.
消费者(8).到达旅游公司.耗时【151】毫秒.开始等待其他参团人员.
消费者(1).到达旅游公司.耗时【335】毫秒.开始等待其他参团人员.
-----------------本次参团人员满,可以出发了.H起来吧-----------------
消费者(1).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(3).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(8).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(5).到达旅游公司.耗时【380】毫秒.开始等待其他参团人员.
消费者(4).到达旅游公司.耗时【571】毫秒.开始等待其他参团人员.
消费者(2).到达旅游公司.耗时【708】毫秒.开始等待其他参团人员.
-----------------本次参团人员满,可以出发了.H起来吧-----------------
消费者(2).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(4).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(5).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(6).到达旅游公司.耗时【881】毫秒.开始等待其他参团人员.
消费者(7).到达旅游公司.耗时【911】毫秒.开始等待其他参团人员.
2.2.案例分析
2.2.1.执行结果分析
总的有8个人参团,每团参团人员限定3人,即3人可成一团。分析执行结果,我们看到首先8个人先后前往旅游公司:
消费者(3).计划报名参团旅游.正前往旅游公司.
消费者(4).计划报名参团旅游.正前往旅游公司.
消费者(7).计划报名参团旅游.正前往旅游公司.
消费者(1).计划报名参团旅游.正前往旅游公司.
消费者(8).计划报名参团旅游.正前往旅游公司.
消费者(2).计划报名参团旅游.正前往旅游公司.
消费者(5).计划报名参团旅游.正前往旅游公司.
消费者(6).计划报名参团旅游.正前往旅游公司.
因大家距离旅游公司有远近,有人先到,有人后到,先到的先拼成团。我们看到:消费者(3)、消费者(8)、消费者(1)最先拼成一团,这时候,CyclicBarrier提示拼团人员满,并放行:
消费者(3).到达旅游公司.耗时【107】毫秒.开始等待其他参团人员.
消费者(8).到达旅游公司.耗时【151】毫秒.开始等待其他参团人员.
消费者(1).到达旅游公司.耗时【335】毫秒.开始等待其他参团人员.
-----------------本次参团人员满,可以出发了.H起来吧-----------------
消费者(1).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(3).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(8).参加的旅游团.人员到齐.开开心心去旅游喽.
紧接着,消费者(5)、消费者(4)、消费者(2)拼成团,CyclicBarrier再次提示拼团人员满,并放行:
消费者(5).到达旅游公司.耗时【380】毫秒.开始等待其他参团人员.
消费者(4).到达旅游公司.耗时【571】毫秒.开始等待其他参团人员.
消费者(2).到达旅游公司.耗时【708】毫秒.开始等待其他参团人员.
-----------------本次参团人员满,可以出发了.H起来吧-----------------
消费者(2).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(4).参加的旅游团.人员到齐.开开心心去旅游喽.
消费者(5).参加的旅游团.人员到齐.开开心心去旅游喽.
最后,我们看到消费者(6)、消费者(7)到达并拼团,但是因为旅游公司限定3人才可成团,还差1人,因此消费者(6)、消费者(7)只能等待,暂时还不能出发旅游:
消费者(6).到达旅游公司.耗时【881】毫秒.开始等待其他参团人员.
消费者(7).到达旅游公司.耗时【911】毫秒.开始等待其他参团人员.
2.2.2.CyclicBarrier关键知识点
通过CyclicBarrier,我们成功实现了旅游公司3人成团的业务编程模型,公司赚了钱老板很开心,老板给你涨了工资你很开心!这是个皆大欢喜的事情。
现在我们回过头来看,使用CyclicBarrier非常简单,只需要两个步骤:
- 根据业务模型,定义CyclicBarrier
/**
* 旅游公司规定的拼团规模,3人一个团(只要凑足了3个人即可出发旅游)
*/
public static CyclicBarrier tripCyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("-----------------本次参团人员满,可以出发了.H起来吧-----------------");
}
});
- 将参与拼团成员,通过await等候其它参团人员
// 等候其他参团人员
System.out.println(name + ".到达旅游公司.耗时【" + time + "】毫秒.开始等待其他参团人员.");
tripCyclicBarrier.await();
// 拼团人员到齐,出发旅游
System.out.println(name + ".参加的旅游团.人员到齐.开开心心去旅游喽.");