多线程进阶学习11------CountDownLatch、CyclicBarrier、Semaphore详解
CountDownLatch
①. CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞
②. 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)
③. 计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行
public static void main(String[] args) throws Exception {CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <= 6; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t");countDownLatch.countDown();}, i + "").start();}countDownLatch.await();System.out.println(Thread.currentThread().getName() + "\t班长关门走人,main线程是班长");}
比如说CountDownLatch你就可以回答:前面提到了CountDownLatch也是基于AQS实现的,它的实现机制很简单,
- 当我们在构建CountDownLatch对象时,传入的值其实就会赋值给 AQS 的关键变量state
- 执行CountDownLatch的countDown方法时,其实就是利用CAS 将state 减一
- 执行await方法时,其实就是判断state是否为0,不为0则加入到阻塞队列中,将该线程阻塞掉(除了头结点),因为头节点会一直自旋等待state为0,当state为0时,头节点把剩余的在队列中阻塞的节点也一并唤醒。
再简单总结下:CountDownlatch基于AQS实现,会将构造CountDownLatch的入参传递至state,countDown()就是在利用CAS将state减-1,
await()实际就是让头节点一直在等待state为0时,释放所有等待的线程
CyclicBarrier
CyclicBarrier原理
1、它没有像CountDownLatch和ReentrantLock使用AQS的state变量,而是使用CyclicBarrier内部维护的内部维护count变量
2、同时CyclicBarrier借助ReentrantLock加上Condition实现等待唤醒的功能
parties变量和condition队列
在构建CyclicBarrier时,传入的值是parties变量,同时也会赋值给CyclicBarrier内部维护count变量(这是可以复用的关键)
//parties表示屏障拦截的线程数量,当屏障撤销时,先执行barrierAction,然后在释放所有线程
public CyclicBarrier(int parties, Runnable barrierAction)
//barrierAction默认为null
public CyclicBarrier(int parties)
每次调用await时,会将count -1 ,操作count值是直接使用ReentrantLock来保证线程安全性
如果count不为0,则添加则condition队列中
如果count等于0时,则把节点从condition队列添加至AQS的队列中进行全部唤醒,并且将parties的值重新赋值为count的值(实现复用)

再简单总结下:CyclicBarrier则利用ReentrantLock和Condition,自身维护了count和parties变量。 每次调用await将count-1,并将线程加入到condition队列上。等到count为0时,则将condition队列的节点移交至AQS队列,并全部释放。
阻塞任务线程而非主线程
CountDownLatch和CyclicBarrier都是线程同步的工具类。可以发现这两者的等待主体是不一样的。
- CountDownLatch调用await()通常是主线程/调用线程
- CyclicBarrier调用await()是在任务线程调用的,所以,CyclicBarrier中的阻塞的是任务的线程,而主线程是不受影响的。
比如集齐7颗龙珠就能召唤神龙。
//集齐7颗龙珠就能召唤神龙
public class CyclicBarrierDemo {public static void main(String[] args) {// public CyclicBarrier(int parties, Runnable barrierAction) {}CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{System.out.println("召唤龙珠");});for (int i = 1; i <=7; i++) {final int temp=i;new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t收集到了第"+temp+"颗龙珠");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}).start();}}
}
共同抵达时的执行操作
屏障的抵达操作:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。
- CyclicBarrier支持一个可选的Runnable barrierAction命令,在一组线程中的最后一个线程到达之后,但在释放所有线程之前运行一次。
- 若在继续所有参与线程之前更新共享状态,此屏障操作很有用。
如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。
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, () -> System.out.println("寝室四兄弟一起出发去球场"));private static class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name + "开始从宿舍出发");try {cb.await();//线程的具体业务操作TimeUnit.SECONDS.sleep(1);System.out.println(name + "从楼底下出发");TimeUnit.SECONDS.sleep(1);System.out.println(name + "到达操场");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {String[] str = {"李明", "王强", "刘凯", "赵杰"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,现在开始打球");} catch (InterruptedException e) {e.printStackTrace();}}
屏障复用
CyclicBarrier是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。可重复利用,说明该类创建的对象可以复用;CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障(Barrier)。
现在对CyclicBarrier进行复用…又来了一拨人,看看愿不愿意一起打:
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, () -> System.out.println("寝室四兄弟一起出发去球场"));private static class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name + "开始从宿舍出发");try {cb.await();TimeUnit.SECONDS.sleep(1);System.out.println(name + "从楼底下出发");TimeUnit.SECONDS.sleep(1);System.out.println(name + "到达操场");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {String[] str = {"李明", "王强", "刘凯", "赵杰"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,现在开始打球");System.out.println();System.out.println("现在对CyclicBarrier进行复用.....");System.out.println("又来了一拨人,看看愿不愿意一起打:");} catch (InterruptedException e) {e.printStackTrace();}String[] str1= {"王二","洪光","雷兵","赵三"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str1[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");} catch (InterruptedException e) {e.printStackTrace();}}
Semaphore
Semaphore 是什么?Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
使用场景:
通常用于那些资源有明确访问数量限制的场景,常用于限流 。
- 比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。
- 比如:停车场场景,车位数量有限,同时只能容纳多少台车,车位满了之后只有等里面的车离开停车场外面的车才可以进入。可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。
- 比如,接口限流 ,应用限流 ,商品限流…
Semaphore常用方法说明
acquire()
获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。tryAcquire(long timeout, TimeUnit unit)
尝试在指定时间内获得令牌,返回获取令牌成功或失败,不阻塞线程。release()
释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。hasQueuedThreads()
等待队列里是否还存在等待线程。getQueueLength()
获取等待队列里阻塞的线程数。drainPermits()
清空令牌把可用令牌数置为0,返回清空令牌的数量。availablePermits()
返回可用的令牌数量。
用semaphore 实现停车场提示牌功能
每个停车场入口都有一个提示牌,上面显示着停车场的剩余车位还有多少,当剩余车位为0时,不允许车辆进入停车场,直到停车场里面有车离开停车场,这时提示牌上会显示新的剩余车位数。
业务场景 :
- 停车场容纳总停车量10。
- 当一辆车进入停车场后,显示牌的剩余车位数响应的减1.
- 每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。
- 停车场剩余车位不足时,车辆只能在外面等待。
public class TestCar {//停车场同时容纳的车辆10private static Semaphore semaphore=new Semaphore(10);public static void main(String[] args) {//模拟100辆车进入停车场for(int i=0;i<100;i++){Thread thread=new Thread(new Runnable() {public void run() {try {System.out.println("===="+Thread.currentThread().getName()+"来到停车场");if(semaphore.availablePermits()==0){System.out.println("车位不足,请耐心等待");}semaphore.acquire();//获取令牌尝试进入停车场System.out.println(Thread.currentThread().getName()+"成功进入停车场");Thread.sleep(new Random().nextInt(10000));//模拟车辆在停车场停留的时间System.out.println(Thread.currentThread().getName()+"驶出停车场");semaphore.release();//释放令牌,腾出停车场车位} catch (InterruptedException e) {e.printStackTrace();}}},i+"号车");thread.start();}}
}
用semaphore 实现防止商品超卖
package com.limiting.semaphore;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;/*** 秒杀防止商品超卖现象*/
public class SemaphoreCommodity {//商品池private Map<String, Semaphore> map=new ConcurrentHashMap<>();//初始化商品池public SemaphoreCommodity() {//手机10部map.put("phone",new Semaphore(10));//电脑4台map.put("computer",new Semaphore(4));}/**** @param name 商品名称* @return 购买是否成功*/public boolean getbuy(String name) throws Exception {Semaphore semaphore = map.get(name);while (true) {int availablePermit = semaphore.availablePermits();if (availablePermit==0) {//商品售空return false;}boolean b = semaphore.tryAcquire(1, TimeUnit.SECONDS);if (b) {System.out.println("抢到商品了");///处理逻辑return true;}}}public static void main(String[] args) throws Exception {SemaphoreCommodity semaphoreCommodity=new SemaphoreCommodity();for (int i = 0; i < 10; i++) {new Thread(()->{try {System.out.println(semaphoreCommodity.getbuy("computer"));} catch (Exception e) {e.printStackTrace();}}).start();}}
}
用semaphore 实现接口限流
切面注解SemaphoreDoc
package com.limiting.semaphore;import java.lang.annotation.*;@Documented@Target({ElementType.METHOD})//作用:方法
@Retention(RetentionPolicy.RUNTIME)
public @interface SemaphoreDoc {String key(); //建议设置不然可能发生,不同方法重复限流现象int limit() default 3;int blockingTime() default 3;}
切面类SemaphoreAop
@Component
@Aspect
public class SemaphoreAop {//这里需要注意了,这个是将自己自定义注解作为切点的根据,路径一定要写正确了@Pointcut(value = "@annotation(com.limiting.semaphore.SemaphoreDoc)")public void semaphoreDoc() {}//限流池private static Map<String, Semaphore> map=new ConcurrentHashMap<>();@Around("semaphoreDoc()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object res = null;MethodSignature signature = (MethodSignature)joinPoint.getSignature();SemaphoreDoc annotation = signature.getMethod().getAnnotation(SemaphoreDoc.class);int blockingTime = annotation.blockingTime();int limit = annotation.limit();String key = annotation.key();StringBuilder name = new StringBuilder(key+signature.getMethod().getName());//方法名for (String parameterName : signature.getParameterNames()) {name.append(parameterName);}Semaphore semaphore = map.get(name.toString());if (semaphore == null) {Semaphore semaphore1 = new Semaphore(limit);map.put(name.toString(),semaphore1);semaphore=semaphore1;}try {//获取令牌boolean b = semaphore.tryAcquire(blockingTime, TimeUnit.SECONDS);if (b) {//如果拿到令牌了那么执行方法try {res = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}} else {//在一定时间内拿不到令牌那么就访问失败throw new Exception("访问超时,目前请求人数过多请稍后在试");}} finally {//释放令牌,腾出位置semaphore.release();}return res;}}
相关文章:
多线程进阶学习11------CountDownLatch、CyclicBarrier、Semaphore详解
CountDownLatch ①. CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞 ②. 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞) ③. 计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行 public static void m…...
华为OD机试用java实现 -【RSA 加密算法】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:RSA 加密算法 题目 RSA 加密…...
技术宅小伙:大龄程序员就业,未来我们将何去何从?
程序员是一个高薪高压的职业,同时也是一个需要不断学习的职业。随着技术的不断更新换代,程序员需要不断地学习新的知识和技能,以适应市场的需求。然而,有些程序员可能会遭遇裁员,失去了稳定的收入来源。有些程序员可能…...
Spring Boot+Vue实现Socket通知推送
目录 Spring Boot端 第一步,引入依赖 第二步,创建WebSocket配置类 第三步,创建WebSocket服务 第四步,创建Controller进行发送测试 Vue端 第一步,创建连接工具类 第二步,建立连接 编辑 第三步&a…...
python---python介绍
python介绍 1.1介绍 1.1.1为什么学习 1.1.2什么是python 优雅简单易学1.1.3在线2进制转换 在线二进制转文本工具 - 转换 1.1.4python的安装和配置 1.需要配置对应的环境变量。可以设置多个。 默认全选 设置安装的路径 最后安装完成即可。 验证:python 如何退出 1.1.…...
第十四届蓝桥杯大赛——真题训练第10天
目录 第一题:扫雷 题目描述 输入描述 输出描述 输入输出样例 运行限制 题目代码 第 2 题:完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 题目分析 题目代码 第三题:求阶乘 问题描述…...
3.29~3.30学习总结
刷题情况: 蓝桥杯刷题: Java学习情况: 抽象类:如果将子类中抽取的共性行为(方法),子类的执行不一样的时候 (通俗一点来说,就是无法找到一个万能的方法体供子类使用,但这…...
结构体详解 ——— C语言
目录 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体实现位段(位段的填充&可移植性) 位段的内存分配 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对…...
Java SE 基础(4) Java的语言特点
语言特点 Java是一门面向对象编程语言,不仅吸收了C语言的各种优点,还摒弃了C里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论&a…...
都炸店了,拼多多还在坚持什么
子超这两天听说了拼多多被“炸店”事件,第一反应是震惊:这都什么年代了,还有这种不择手段的暴力行为?所谓的炸店,就是一些人员被煽动和组织起来,有预谋地对店铺发起打砸行动,这和线下去打砸商铺…...
vue尚品汇商城项目-day01【6.Footer组件的显示与隐藏】
文章目录6.Footer组件的显示与隐藏6.1我们可以根据组件身上的$route获取当前路由的信息,通过路由路径判断Footer显示与隐藏6.2配置路由的时候,可以给路由添加元信息[meta],路由需要配置对象,它的key不能乱接、瞎写、胡写ÿ…...
命令行上的数据科学第二版 一、简介
原文:https://datascienceatthecommandline.com/2e/chapter-1-introduction.html 贡献者:Ting-xin 这本书是关于如何利用命令行做数据科学的。我的目标是通过教你使用命令行的力量,让自己成为一名更高效和多产的数据科学家。 在标题中同时使…...
utf-8转换到utf-16的转换过程你懂吗?
人生自是有情痴,此恨不关风与月。——唐代元稹《离思》 从UTF-8编码的文件中读取文本并将其存储到Java的String对象中,涉及到从字节序列到Unicode码点,再到UTF-16编码的转换。以下是详细的步骤: 从文件读取字节序列:首…...
C++编程大师之路:从入门到精通--持续更新中~
文章目录前言主要内容C基础入门初识C第一个C程序注释变量常量关键字标识符命名规则数据类型整型sizeof关键字实型(浮点型)字符型转义字符字符串型布尔类型 bool数据的输入运算符算术运算符赋值运算符比较运算符逻辑运算符程序流程结构选择结构if语句三目…...
面试阿里软件测试岗,收到offer后我却毫不犹豫拒绝了....
我大学学的是计算机专业,毕业的时候,对于找工作比较迷茫,也不知道当时怎么想的,一头就扎进了一家外包公司,一干就是2年。我想说的是,但凡有点机会,千万别去外包! 在深思熟虑过后&am…...
【c语言多线程编程】关于pthread_create()和pthread_join()的多线程详解
关于pthread_create()和pthread_join()的多线程详解 一、首先说一下pthread_create() 函数的用法: int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);各参数的含义: 1、pthread_t *thre…...
抖音seo矩阵系统源码搭建技术+二开开源代码定制部署
抖音已经成为了当今最为流行的短视频平台之一,拥有着庞大的用户群体和海量的视频资源。对于一些商家或者运营者来说,如何从这些视频资源中挖掘出有效的信息,进而提升自己的品牌、产品或者内容的曝光度,就成为了一个非常重要的问题…...
【周赛刷题】平衡树+图中最短环
2612. 最少翻转操作数(平衡树) 题目的难度有一部分在于数学推导。对于某个点 iii 进行反转是有一个范围的,这个范围需要考虑到边界的情况。可以的得到的一个结论是。对于窗口反转,KaTeX parse error: Expected group after ^ at p…...
C++笔记——第十篇 继承 的解析,详细易懂哦
目录 一、继承的概念及定义 1.继承的概念 2. 继承定义 2.1定义格式 2.2继承关系和访问限定符 2.3继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承…...
SQL Server中的全文搜索
SQL Server中的全文搜索一、概述二、全文搜索查询三、将全文搜索查询与 LIKE 谓词进行比较四、全文搜索体系结构4.1、SQL Server 进程4.2、过滤器守护程序主机进程五、全文搜索处理5.1、全文索引过程5.2、全文查询流程六、全文索引体系结构6.1、全文索引结构6.2、全文索引片段6…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
