JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析

拼多多2面,还是模拟拼团,要求用户拼团成功后,提交订单支付金额。

        之前我们在系列(8)《CountDownLatch核心原理》,实现过拼团场景。但是CountDownLatch里调用countDown()方法后,线程还是可以继续执行后面的代码,没有真正的阻塞。

1、面试真题:完善模拟拼团

       这里我们应用循环屏障CyclicBarrier,可以控制一组线程到达屏障点后,再全部继续执行,而且这个屏障可以重复利用的特性来实现这个场景。

      现在我们模拟2人拼团成功的场景,每满2人就允许提交订单支付,且后台发送消息给仓库发货。

package lading.java.mutithread;import cn.hutool.core.date.DateTime;import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
/*** 模拟拼团,并通知仓库发货*/
public class Demo010CyclicBarrier {public static int count = 2;//要求达到屏障数量public static volatile ConcurrentHashMap<String, String> customerNames = new ConcurrentHashMap<>();//记录已到达屏障客户线程名public static CyclicBarrier barrier = new CyclicBarrier(count, new sendMsg());//屏障数量2,目标线程数量达到后,执行sendMsgpublic static void main(String[] args) {//模拟5个人拼团for (int i = 0; i < 5; i++) {new Thread(() -> {try {Thread.sleep((new Random().nextInt(10 - 1 + 1)) * 1000);//客户浏览商品信息NsSystem.out.println(DateTime.now().toString("YYYY-MM-dd hh:mm:ss SSS") +" 【"+ Thread.currentThread().getName() + "】,到达屏障");customerNames.put(Thread.currentThread().getName(), Thread.currentThread().getName());//本批次拼团名单barrier.await();//到达屏障,进入阻塞System.out.println(DateTime.now().toString("YYYY-MM-dd hh:mm:ss SSS") +" 【"+ Thread.currentThread().getName() + "】,完成支付。");} catch (InterruptedException e) {throw new RuntimeException(e);} catch (BrokenBarrierException e) {throw new RuntimeException(e);}}, "客户00" + (i + 1)).start();}}static class sendMsg extends Thread {@Overridepublic void run() {System.out.println(DateTime.now().toString("YYYY-MM-dd hh:mm:ss") + customerNames.keySet() + "达到屏障拼团人数,允许提交订单!");customerNames.clear();//本批次拼团名单清空}}
}

客户2,3到达后,任务线程就发送信息给参考发货,同时客户2、3可以支持订单。当客户1到达后,就阻塞等待客户4拼团才继续执行。最后客户5,因为没有成团,一直阻塞等待。

2、说说CyclicBarrier的核心原理

       CyclicBarrier,顾名思义就是循环屏障。支持一组多个线程相互等待,线程调用了屏障的await()方法后,原地阻塞等待。当本组线程最后一个到达屏障后,本组其他线程全部被唤醒,继续执行屏障await()方法后面代码。

      由于这个屏障在释放完本组等待线程后,可以重复使用,等待下一组线程过来阻塞排队,因此称为:循环屏障。

3、具体说说CyclicBarrier怎么使用

        循环屏障也是很简单,核心方法就几个。

1.CyclicBarrier(int parties, Runnable barrierAction)

       就是实例化一个循环屏障,parties就是本组线程目标数量。barrierAction就有意思了,这个是可选参数。如果本组线程都到达屏障后,就先执行这个Runnable barrierAction,阻塞等待的线程才能继续执行。可以从模拟拼团实例运行结果看到:线程2、3到达屏障后,先执行sendMsg的方法,线程2、3才可以开始支付。

2.await()

       线程调用这个方法后,表示已经到达屏障,该线程阻塞进入休眠状态,等本组其他线程都到达屏障点,才会被唤醒继续执行后面的代码。还有两个入参可选,await(long timeout, TimeUnit unit) 如果超出指定的等待时间,则抛出TimeoutException异常。

核心常用就这2个方法了,没别的!

4、CyclicBarrier源码分析

       首先,看一下CyclicBarrier的成员变量,里面int 有parties、和count。这两个变量成功支持了屏障变成循环屏障。其中parties表示屏障阈值,count表示当前还差多少个线程到达屏障,每来一个线程调用await(),本组线程的屏障count就减1.count为0时候,就唤醒本组线程继续执行。

    private final ReentrantLock lock = new ReentrantLock();private final Condition trip = lock.newCondition();private final int parties;private final Runnable barrierCommand;private Generation generation = new Generation();private int count;

       其次,看一下实例化循环屏障对象代码,重点将本组循环等待线程数量parties赋值给parties、count,以及设置屏障任务。

    public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;this.barrierCommand = barrierAction;}//不指定屏障任务public CyclicBarrier(int parties) {this(parties, null);}

      然后,重点看一下await()阻塞等待的方法,里面调用了dowait()方法。

         源码很长,简单总结:dowait方法就是更新count-1,表示本组又报道了一个线程,还没到的名额少了一个。如果count为0,那说明自己是本组最后来屏障集合的线程,负责唤醒大家,以及执行屏障的barrierCommand任务(如果有的话)。

        源码细的说:除了count-1判断是否全部到齐,如果是0,包括如何唤醒其他线程。不是0,如何陷入阻塞等待。

具体就是:

1、如果count不是0,就把自己加入到AQS的条件队列里,等待信号唤醒。

2、如果count是0,说明本线程是最后一个到达的,咱不用进入阻塞,先执行屏障的barrierCommand,然后去唤醒本组的其他线程兄弟继续执行。并重置count值为parties阈值,方便下一组线程使用,达成屏障可循环使用的目的。

       其他就是在AQS里如何阻塞等待、以及唤醒其他线程具体逻辑,源码有点复杂,等后续我们出源码分析专栏,就画图分析讲解。

 private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;//加锁,去更新count,这里就不是CAS了lock.lock();try {final Generation g = generation;//判断屏障是否被中断if (g.broken)throw new BrokenBarrierException();//判断本线程是否已中断if (Thread.interrupted()) {//本组线程的屏障中断,并重置屏障breakBarrier();throw new InterruptedException();}// 对count减1int index = --count;// 本组屏障全部线程都到达屏障,接下来执行屏障任务、以及唤醒本组其他阻塞兄弟if (index == 0) {  // trippedboolean ranAction = false;try {final Runnable command = barrierCommand;//实例CyclicBarrier(),如果有指定任务,本线程就代劳去执行if (command != null)command.run();ranAction = true;nextGeneration();return 0;} finally {if (!ranAction)breakBarrier();}}// 如果不为0,说明约定到本屏障的其他兄弟们还没到齐,那就自旋等待,直到被打断或者超时for (;;) {try {if (!timed)trip.await();else if (nanos > 0L)nanos = trip.awaitNanos(nanos);} ........}

今天就分享这么多,明天我们继续分享并发编程里的Condition条件接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/145606.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2024年华为认证热门的5个方向

华为认证是ICT领域内广受认可的专业资格认证体系&#xff0c;它为不同层次的ICT专业人士提供了多样化的认证路径。华为认证体系主要分为三个等级&#xff1a;HCIA&#xff08;华为认证ICT工程师&#xff09;、HCIP&#xff08;华为认证ICT高级工程师&#xff09;、HCIE&#xf…

HTML/CSS/JS学习笔记 Day6(CSS--C3 背景样式)

跟着该视频学习&#xff0c;记录笔记&#xff1a;【黑马程序员pink老师前端入门教程&#xff0c;零基础必看的h5(html5)css3移动端前端视频教程】https://www.bilibili.com/video/BV14J4114768?p12&vd_source04ee94ad3f2168d7d5252c857a2bf358 Day6 内容梳理&#xff1a;…

【永磁同步电机(PMSM)】 2. 数学模型

【永磁同步电机&#xff08;PMSM&#xff09;】 2. 数学模型 1. 模型假设和磁路电路分析1.1 模型假设1.2 磁路分析—磁链方程1.3 电路分析—电压方程1.4 机械分析—运动方程 2. 三相静止坐标系的数学模型2.1 电压方程2.2 磁链方程2.3 电磁转矩方程2.4 电机机械运动方程 3. 变换…

webpack4 target:“electron-renderer“ 打包加速配置

背景 昨天写得一篇Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题-CSDN博客文章浏览阅读754次&#xff0c;点赞19次&#xff0c;收藏11次。因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vu…

Cypress初次安装启动常见问题

安装成功后会出现如图所示目录和文件 Cypress启动问题 进入node_modules下的.bin 目录,执行命令: cypress open 启动cypress&#xff0c;此时会有一个报错&#xff0c;如图 需要进入项目目录&#xff0c;编辑package.json文件中scripts配置 此时再次启动CMD&#xff0c;进入根…

揭开 Vue 3 中大量使用 ref 的隐藏危机

在 Vue 3 中&#xff0c;ref 是用来创建响应式的引用&#xff0c;它能够追踪和管理单一的变量或对象。当代码中大量使用 ref 时&#xff0c;虽然可以实现对各个状态或数据的精细控制&#xff0c;但也会带来一些问题和潜在影响。 1. 大量使用 ref 带来的问题 1、代码冗长与维护…

第10章 面向对象编程(高级部分)

第10章 面向对象编程(高级部分) 文章目录 第10章 面向对象编程(高级部分)10.1 类变量和类方法10.1.1 类变量-提出问题10.1.2 传统的方法来解决10.1.3 类变量快速入门10.1.4 类变量内存布局10.1.5 什么是类变量10.1.6 如何定义类变量10.1.8 类变量使用注意事项和细节讨论10.1.9 …

JavaScript - Document文档操作

1. 前言 ​​​​​​​ 编写网页时&#xff0c;我们需要时刻操作文档进而完成我们想要的效果。这就是通过文档对象模型实现&#xff0c;使用Document对象控制HTML以及样式信息的API 2. Document的树结构 在了解Document文档对象模型之前&#xff0c;我们先了解Dom的树结构 …

pg入门1——使用容器启动一个pg

1. 下载pg镜像 地址&#xff1a; https://hub.docker.com/r/bitnami/postgresql 下载镜像&#xff1a; docker pull bitnami/postgresql:16.3-alpine3.20 2. 运行镜像 docker run -e POSTGRES_PASSWORDAb123456! -d bitnami/postgresql:16.3-alpine3.20 3. 查看、进入容…

如何使用VMware安装Linux操作系统

使用VMware安装Linux操作系统。以Cont OS为例进行说明。 准备工作&#xff1a; 下载并安装VMware&#xff1a; 下载地址&#xff1a;https://support.broadcom.com/ 下载好VMware&#xff0c;就是吧下载好的VMware安装&#xff0c;只需要“下一步”就行&#xff0c;只是到路径…

惠海H6118 DC-DC 降压恒流芯片30V36v40V48V降12V9V24V36V 1.2A大电流 调光降压芯片IC舞台灯

H6118是一款连续电感电流导通模式的降压型LED恒流驱动器&#xff0c;用于驱动一个或多个LED灯串。H6118工作电压从4V到30V&#xff0c;提供可调的输出电流&#xff0c;最大输出电流可达到1.2A。 H6118内置功率开关管&#xff0c;采用高端电流检测电路&#xff0c;支持PWM模式调…

即时通讯框架MobileIMSDK的H5端开发快速入门

► 相关链接&#xff1a; ① MobileIMSDK-H5端的详细介绍② MobileIMSDK-H5端的开发手册new&#xff08;* 精编PDF版&#xff09; 一、技术准备 您是否已对Web端即时通讯技术有所了解&#xff1f; 1&#xff09;新手入门贴&#xff1a;史上最全Web端即时通讯技术原理详解2&…

提前解锁 Vue 3.5 的新特性

Vue 3.5 是 Vue.js 新发布的版本&#xff0c;虽然没有引入重大变更&#xff0c;但带来了许多实用的增强功能、内部优化和性能改进。 1. 响应式系统优化 Vue 3.5 进一步优化了响应式系统的性能&#xff0c;并且减少内存占用。尤其在处理大型或深度嵌套的响应式数组时&#xff…

【论文阅读】Grounding Language with Visual Affordances over Unstructured Data

Abstract 最近的研究表明&#xff0c;大型语言模型&#xff08;llms&#xff09;可以应用于将自然语言应用于各种各样的机器人技能。然而&#xff0c;在实践中&#xff0c;学习多任务、语言条件机器人技能通常需要大规模的数据收集和频繁的人为干预来重置环境或帮助纠正当前的…

unix中如何查询和修改进程的资源限制

一、前言 一个进程在运行时&#xff0c;会用到各种资源&#xff0c;比如cpu的使用时间、内存空间、文件等等。那么&#xff0c;一个进程能够占用多少资源呢&#xff1f;cpu使用的时间有多长&#xff1f;进程空间有多大&#xff1f;能够创建多少个文件&#xff1f;这个就是本文…

【数据结构】设有一带头结点的单链表,编程将链表颠倒过来。要求不用另外的数 组或结点完成。

编程题: 设有一带头结点的单链表,编程将链表颠倒过来。要求不用另外的数 组或结点完成。 分析: 该算法通过维护三个指针(prev、curr 和 next)逐步遍历单链表,实现链表的逆转。在遍历过程中,curr 的 next 指针被更新为指向 prev,逐步反转指向。最终,头结点的 next 指针…

用户态缓存:高效数据交互与性能优化

目录 1. 用户态缓存区工作背景 1.1 为什么每条连接都需要读写缓存区 1.1.1 读缓存区&#xff08;Read Buffer&#xff09; 1.1.2 写缓存区&#xff08;Write Buffer&#xff09; 1.2 用户态缓存区的工作流程 1.3 用户态缓存区的重要性 2. UDP 和 TCP 的设计差异 2.1 UD…

Python模块和包:标准库模块(os, sys, datetime, math等)②

文章目录 一、os 模块1.1 获取当前工作目录1.2 列出目录内容1.3 创建和删除目录1.4 文件和目录操作 二、sys 模块2.1 获取命令行参数2.2 退出程序2.3 获取 Python 版本信息 三、datetime 模块3.1 获取当前日期和时间3.2 日期和时间的格式化3.3 日期和时间的运算 四、math 模块4…

私有化聊天软件成为企业内部通讯新趋势

在数字化转型浪潮的推动下&#xff0c;企业对于高效、安全、灵活的内部通讯需求日益增长。传统的电子邮件、电话会议等沟通方式已难以满足现代企业对即时性、协作性和信息安全性的要求。因此&#xff0c;私有化聊天工具作为企业内部通讯的定制化解决方案&#xff0c;正逐渐成为…

我的数据库第一课:从懵懂到启迪

我的数据库第一课&#xff1a;从懵懂到启迪 前言 在数字化浪潮席卷全球的今天&#xff0c;数据库作为IT技术的“活化石”&#xff0c;已经成为不可或缺的基础设施。特别是在国内&#xff0c;随着经济的飞速发展和信息化建设的推进&#xff0c;数据库技术也经历了从无到有、从…