并发编程——JUC并发工具

文章目录

  • 前言
  • CountDownLatch
    • CountDownLatch应用
    • CountDownLatch核心源码
  • Semaphore
    • Semaphore应用
    • Semaphore核心源码
  • CyclicBarrier
    • CyclicBarrier应用
    • CyclicBarrier核心源码
  • 总结

在这里插入图片描述

前言

JUC 是Java并发编程工具类库,提供了一些常用的并发工具,例如锁、信号量、计数器、事件循环、线程池、并发集合等。这些工具可以帮助开发人员简化并发编程的复杂性,提高程序效率和可靠性。

CountDownLatch

CountDownLatch应用

CountDownLatch本身就好像一个计数器,可以等待一个或多个线程完成后再执行,其基于AQS实现。

public static void main(String[] args) throws InterruptedException, BrokenBarrierException {CountDownLatch countDownLatch = new CountDownLatch(3);new Thread(() -> {System.out.println("111");countDownLatch.countDown();}).start();new Thread(() -> {System.out.println("222");countDownLatch.countDown();}).start();new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("333");countDownLatch.countDown();}).start();// 主线会阻塞在这个位置,直到CountDownLatch的state变为0countDownLatch.await();System.out.println("main");
}

CountDownLatch核心源码

// CountDownLatch 的有参构造
public CountDownLatch(int count) {// 健壮性校验if (count < 0) throw new IllegalArgumentException("count < 0");// 构建Sync给AQS的state赋值this.sync = new Sync(count);
}

countDown方法,本质就是调用了AQS的释放共享锁操作

public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {// 唤醒在AQS队列中排队的线程。doReleaseShared();return true;}return false;
}// countDownLatch实现的业务
protected boolean tryReleaseShared(int releases) {for (;;) {int c = getState();if (c == 0)return false;// state - 1int nextc = c-1;// 用CAS赋值if (compareAndSetState(c, nextc))return nextc == 0;}
}
// 如果CountDownLatch中的state已经为0了,那么再次执行countDown跟没执行一样。
// 而且只要state变为0,await就不会阻塞线程。

功能都是AQS提供的,只有tryReleaseShared需要实现的类自己去编写业务。

await方法,调用了AQS提供的获取共享锁并且允许中断的方法

// await方法
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}// AQS获取共享锁并且允许中断的方法
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();// countDownLatch操作if (tryAcquireShared(arg) < 0)// 如果返回的是-1,代表state肯定大于0doAcquireSharedInterruptibly(arg);
}// CountDownLatch实现的tryAcquireShared
protected int tryAcquireShared(int acquires) {// state为0,返回1,。否则返回-1return (getState() == 0) ? 1 : -1;
}// 让当前线程进到AQS队列,排队去
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {// 将当前线程封装为Node,并且添加到AQS的队列中final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head) {// 再次走上面的tryAcquireShared,如果返回的是的1,代表state为0int r = tryAcquireShared(arg);if (r >= 0) {// 会将当前线程和后面所有排队的线程都唤醒。setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}
}

Semaphore

Semaphore应用

Semaphore一般用于流控。比如有一个公共资源,多线程都可以访问时,Semaphore可以当作信号量做限制。每当有一个线程获取连接对象时,对信号量-1,当这个线程归还资源时对信号量+1。如果线程拿资源时,发现Semaphore内部的资源个数为0,就会被阻塞。

public static void main(String[] args) throws InterruptedException, BrokenBarrierException {// 声明信号量Semaphore semaphore = new Semaphore(1);// 能否去拿资源semaphore.acquire();// 拿资源处理业务System.out.println("main");// 归还资源semaphore.release();
}

Semaphore核心源码

Semaphore有公平和非公平两种竞争资源的方式。

// 
public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}// 设置资源个数,State其实就是信号量的资源个数
Sync(int permits) {setState(permits);
}

在调用 acquire 获取资源时也是基于AQS提供的获取共享锁方法。

release就是将state+1,归还资源。

// 两个一起 阿巴阿巴
public void release() {sync.releaseShared(1);
}public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {// 唤醒在AQS中排队的Node,去竞争资源doReleaseShared();return true;}return false;
}// 信号量实现的归还资源
protected final boolean tryReleaseShared(int releases) {for (;;) {// 拿stateint current = getState();// state + 1int next = current + releases;// 资源最大值,再+1,变为负数if (next < current)throw new Error("Maximum permit count exceeded");// CAS 改一手if (compareAndSetState(current, next))return true;}
}

共享锁在释放资源后,如果头节点为0,无法确认真的没有后继节点。如果头节点为0,需要将头节点的状态修改为-3,当最新拿到锁资源的线程,查看是否有后继节点并且为共享锁,就唤醒排队的线程

CyclicBarrier

CyclicBarrier应用

CyclicBarrier 一般称为栅栏,和CountDownLatch很像。CountDownLatch在操作时,只能使用一次,也就是state变为0之后,就无法再使用了。CyclicBarrier是可以复用的,他的计数器可以归位,然后再处理。而且可以在计数过程中出现问题后,重置当前CyclicBarrier,再次重新操作!

public static void main(String[] args) throws InterruptedException, BrokenBarrierException {// 声明栅栏CyclicBarrier barrier = new CyclicBarrier(3,() -> {System.out.println("开始!");});new Thread(() -> {System.out.println("第一位选手到位");try {barrier.await();System.out.println("第一位往死里跑!");} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {System.out.println("第二位选手到位");try {barrier.await();System.out.println("第二位也往死里跑!");} catch (Exception e) {e.printStackTrace();}}).start();System.out.println("裁判已经到位");barrier.await();
}

CyclicBarrier核心源码

CyclicBarrier没有直接使用AQS,而是使用ReentrantLock,间接的使用AQS

// CyclicBarrier的有参
public CyclicBarrier(int parties, Runnable barrierAction) {// 健壮性判断!if (parties <= 0) throw new IllegalArgumentException();// parties是final修饰的,需要在重置时,使用!this.parties = parties;// count是在执行await用来计数的。this.count = parties;// 当计数count为0时 ,先执行这个Runnnable!在唤醒被阻塞的线程this.barrierCommand = barrierAction;
}

线程执行await方法,会对count-1,再判断count是否为0,如果不为0,需要添加到AQS中的ConditionObject的Waiter队列中排队,并park当前线程。如果为0,证明线程到齐,需要执行nextGeneration,会先将Waiter队列中的Node全部转移到AQS的队列中,没有后继节点设置为0。然后重置count和broker标记。等到unlock执行后,每个线程都会被唤醒。

private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {// 相当于synchronized中使用wait和notifyfinal ReentrantLock lock = this.lock;lock.lock();try {// 里面就是boolean,默认falsefinal Generation g = generation;// 判断之前栅栏加入线程时,是否有超时、中断等问题,如果有,设置boolean为true,其他线程再进来,直接凉凉if (g.broken)throw new BrokenBarrierException();if (Thread.interrupted()) {breakBarrier();throw new InterruptedException();}// 对计数器count--int index = --count;// 如果--完,是0,代表突破栅栏,干活!if (index == 0) {  // 默认falseboolean ranAction = false;try {// 如果你用的是2个参数的有参构造,说明你传入了任务,index == 0,先执行CyclicBarrier有参的任务final Runnable command = barrierCommand;if (command != null)command.run();// 设置为trueranAction = true;nextGeneration();return 0;} finally {if (!ranAction)breakBarrier();}}// --完之后,index不是0,代表还需要等待其他线程for (;;) {try {// 如果没设置超时时间。  await()if (!timed)trip.await();// 设置了超时时间。  await(1,SECOND)else if (nanos > 0L)nanos = trip.awaitNanos(nanos);} catch (InterruptedException ie) {if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {Thread.currentThread().interrupt();}}if (g.broken)throw new BrokenBarrierException();if (g != generation)return index;if (timed && nanos <= 0L) {breakBarrier();throw new TimeoutException();}}} finally {lock.unlock();}
}// 挂起线程
public final void await() throws InterruptedException {// 允许中断if (Thread.interrupted())throw new InterruptedException();// 添加到队列(不是AQS队列,是AQS里的ConditionObject中的队列)Node node = addConditionWaiter();int savedState = fullyRelease(node);int interruptMode = 0;while (!isOnSyncQueue(node)) {// 挂起当前线程LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}
}// count到0,唤醒所有队列里的线程线程
private void nextGeneration() {// 这个方法就是将Waiter队列中的节点遍历都扔到AQS的队列中,真正唤醒的时机,是unlock方法trip.signalAll();// 重置计数器count = parties;// 重置异常判断generation = new Generation();
}

总结

使用这些工具类时需要注意:

  • Semaphore的使用要避免死锁和过度同步导致的性能问题。
  • CyclicBarrier在屏障点之后的代码要保证所有线程都能正确执行,否则可能导致部分线程一直等待。
  • CountDownLatch的countDown方法要保证在所有线程执行完毕之前被调用,否则可能导致部分线程一直等待。

根据具体的应用场景选择合适的工具类,正确使用并合理设计并发策略,可以提高程序的效率和可靠性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

月木学途开发 5.轮播图模块

概述 效果图 数据库设计 轮播图表 DROP TABLE IF EXISTS banner; CREATE TABLE banner (bannerId int(11) NOT NULL AUTO_INCREMENT,bannerUrl longtext,bannerDesc varchar(255) DEFAULT NULL,bannerTypeId int(11) DEFAULT NULL,PRIMARY KEY (bannerId) ) ENGINEInnoDB AU…

一文看懂这些海外社媒平台属性,跨境外贸必看

随着社交媒体平台的普遍使用&#xff0c;在平台上营销品牌形象、投放广告已经成为销售转化的强大动力&#xff0c;我们普遍熟络的都是国内平台&#xff0c;那么对于跨境外贸的小伙伴来说&#xff0c;熟悉海外社媒平台更加重要&#xff01; 当然仅仅用一个社交媒体平台获得流量的…

【办公小神器】:快速批量转换Word、Excel、PPT为PDF脚本!

文章目录 ✨哔哩吧啦✨脚本使用教程✨温馨小提示设置&#x1f4da;资源领取 专栏Python零基础入门篇&#x1f525;Python网络蜘蛛&#x1f525;Python数据分析Django基础入门宝典&#x1f525;小玩意儿&#x1f525;Web前端学习tkinter学习笔记Excel自动化处理 ✨哔哩吧啦 前…

SpringBoot+MyBatisPlus+MySQL不能储存(保存)emoji表情问题解决

1.之前在学习过程中不知道utf8和utf8mb4的区别&#xff0c;也没过多去了解&#xff0c;直到最近设置的数据库编码全是utf8后发现问题所在了&#xff0c;居然不能储存表情包&#xff01;&#xff01;&#xff01;整个人直接傻了&#xff0c;后面知道了utf8是3字节不能储存表情&a…

计算机视觉与深度学习-全连接神经网络-训练过程-模型正则与超参数调优- [北邮鲁鹏]

目录标题 神经网络中的超参数学习率超参数优化方法网格搜索法随机搜索法 超参数搜索策略粗搜索精搜索 超参数的标尺空间 神经网络中的超参数 超参数 网络结构&#xff1a;隐层神经元个数&#xff0c;网络层数&#xff0c;非线性单元选择等优化相关&#xff1a;学习率、dorpou…

Kubernetes(k8s)上搭建一主两从的mysql8集群

Kubernetes上搭建一主两从的mysql8集群 环境准备搭建nfs服务器安装NFS暴露nfs目录开启nfs服务器 安装MySQL集群创建命名空间创建MySQL密码的Secret安装MySQL主节点创建pv和pvc主节点的配置文件部署mysql主节点 安装第一个MySQL Slave节点创建pv和pvc第一个从节点配置文件部署my…

高质量AI数据服务铺路架桥,云测数据引领行业大模型训练新范式

大模型发展风起云涌&#xff0c;使得AI应用又成为了市场热点。但这场创新运动和上一轮AI热潮的背景不同&#xff0c;如今行业不缺技术、也不乏商业模式健康的玩家&#xff0c;最稀缺的资源&#xff0c;已然变成了高质量数据。大模型的模型从何而来&#xff1f;本质上&#xff0…

计算机竞赛 深度学习+opencv+python实现昆虫识别 -图像识别 昆虫识别

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数&#xff1a;2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 4 MobileNetV2网络5 损失函数softmax 交叉熵5.1 softmax函数5.2 交叉熵损失函数 6 优化器SGD7 学…

iOS“超级签名”绕过App Store作弊解决方案

一直以来&#xff0c;iOS端游戏作弊问题都是游戏行业的一大痛点。在当下游戏多端互通的潮流下&#xff0c;游戏作为一个整体&#xff0c;无论哪一端出现安全问题&#xff0c;都会造成更加严重的影响。因此&#xff0c;iOS端游戏安全保护也同样十分重要。 iOS独特的闭源生态&am…

ATFX汇市:美联储宣布维持利率不变,鲍威尔继续发表鹰派言论

ATFX汇市&#xff1a;今日凌晨02:00&#xff0c;美联储公布9月利率决议结果&#xff0c;宣布维持5.25%5.5%的联邦基金利率区间不变。2:002:05&#xff0c;美元指数从最低104.75飙涨至最高105.21&#xff0c;对应EURUSD的汇率从最高1.0727下跌至最低1.0674&#xff0c;跌幅53基点…

基础组件(线程池、内存池、异步请求池、Mysql连接池)

文章目录 1、概述2、线程池2、异步请求池3、内存池 1、概述 池化技术&#xff0c;减少了资源创建次数&#xff0c;提高了程序响应性能&#xff0c;特别是在高并发场景下&#xff0c;当程序7*24小时运行&#xff0c;创建资源可能会出现耗时较长和失败等问题&#xff0c;池化技术…

小谈设计模式(5)—开放封闭原则

小谈设计模式&#xff08;5&#xff09;—开放封闭原则 专栏介绍专栏地址专栏介绍 开放封闭原则核心思想关键词概括扩展封闭 解释抽象和接口多态 代码示例代码解释 优缺点优点可扩展性可维护性可复用性高内聚低耦合 缺点抽象设计的复杂性需要预留扩展点可能引入过度设计 总结 专…

软件工程第一次作业参考答案

题目 名词解释&#xff1a;软件危机、软件、软件工程、软件生命周期、瀑布模型、原型模型、增量模型、喷泉模型、敏捷过程模型。 答案 软件危机&#xff1a;软件危机是指在软件开发过程中所面临的一系列问题和挑战&#xff0c;包括成本超支、进度延误、质量不达标等。 软件…

ubuntu 22.04 服务器网卡无IP地址

ssh连接服务器连接不上&#xff0c;提示如下&#xff1b; 连接显示器&#xff0c;ip addr ls 命令查看IP地址&#xff0c;有网卡但没有IP地址 solution&#xff1a; sudo dhclient enp10s0用于通过 DHCP 协议获取网络配置信息并为名为 enp10s0 的网络接口分配 IP 地址,enp1…

TiDB 7.1.0 LTS 特性解读丨关于资源管控 (Resource Control) 应该知道的 6 件事

TiDB 7.1.0 LTS 在前段时间发布&#xff0c;相信很多同学都已经抢先使用了起来&#xff0c;甚至都已然经过一系列验证推向了生产环境。面对 TiDB 7.1 若干重要特性&#xff0c;新 GA 的资源管控 (Resource Control) 是必须要充分理解、测试的一个重量级特性。对于常年奋斗在一线…

一个热爱自动驾驶但妥妥外行之人的思考-2023

时间节点为2023年9月&#xff0c;有效期当下及过往&#xff0c;不含未来。 这些年准确说从10年就很关注自动驾驶行业&#xff0c;包括物流/机器人/汽车等。 也和行业内&#xff0c;行业外的朋友做了大量的交流。点滴沟通放在如下链接中&#xff1a; 动态 - CSDN 一些过去的观…

新型智慧公厕“1+3+N”架构,平台、系统、应用的创新

近年来&#xff0c;随着人民生活水平的提高&#xff0c;人们对公共设施的要求也越来越高。其中&#xff0c;如厕问题一直是人们关注的焦点&#xff0c;但传统的公厕设施已经不能满足人们对干净、舒适、安全的需求&#xff0c;这促使了新型智慧公厕的诞生与应用&#xff0c;以如…

八月份跳槽了,历经华为测开岗4轮面试,不出意外,被刷了...

大多数情况下&#xff0c;测试员的个人技能成长速度&#xff0c;远远大于公司规模或业务的成长速度。所以&#xff0c;跳槽成为了这个行业里最常见的一个词汇。 前几天&#xff0c;我看到有朋友留言说&#xff0c;他在面试华为的测试开发工程师的时候&#xff0c;灵魂拷问三小…

c++ 纯虚函数、抽象类

一、 纯虚函数 抽象类 只要有一个纯虚函数&#xff0c;这个类称为抽象类 抽象类的特点 1、无法实例化 2、抽象类的子类&#xff0c;必须要重写父类中的纯虚函数&#xff0c;否者也属于抽象类 例子一 #include <iostream> #include <string.h> using namespa…

MySQL数据库详解 三:索引、事务和存储引擎

文章目录 1. 索引1.1 索引的概念1.2 索引的作用1.3 如何实现索引1.4 索引的缺点1.5 建立索引的原则依据1.6 索引的分类和创建1.6.1 普通索引1.6.2 唯一索引1.6.3 主键索引1.6.4 组合索引1.6.5 全文索引 1.7 查看索引1.8 删除索引 2. 事务2.1 事务的概念2.2 事务的ACID特性2.2.1…