Java - AQS-CountDownLatch实现类(二)
前言
在Java中,AbstractQueuedSynchronizer(简称AQS)是一个用于实现同步器的抽象类,它为实现各种类型的同步器(如锁、信号量等)提供了基本的框架。AQS通过一个双向队列(等待队列)和一个整数变量(状态)来管理线程的排队和状态控制。
AQS的具体实现类主要有以下几种:
ReentrantLock:可重入锁的实现类,支持独占模式。ReentrantReadWriteLock:可重入读写锁的实现类,支持共享和独占模式。Semaphore:信号量的实现类,用于控制同时访问某个资源的线程数量。CountDownLatch:倒计时门闩的实现类,用于等待一组线程完成某个操作后再执行。CyclicBarrier:循环屏障的实现类,用于等待一组线程都到达某个状态点后再一起继续执行。Phaser:分阶段屏障的实现类,用于协调多个线程在不同阶段的同步。LockSupport:用于创建锁和其他同步类的基本线程阻塞原语。
其中,ReentrantLock和ReentrantReadWriteLock是比较常用的同步器实现类,用于提供可重入的独占锁和读写锁。Semaphore、CountDownLatch、CyclicBarrier和Phaser等同步器类则在特定场景下发挥重要作用,帮助线程协调、控制和同步操作。
这些AQS实现类可以根据具体的需求选择使用,通过继承AbstractQueuedSynchronizer类并实现其中的抽象方法,可以定制和扩展自定义的同步器。
CountDownLatch
简介
CountDownLatch是Java中的一个同步辅助类,用于实现线程间的等待和通知机制。它通过一个计数器来实现,该计数器被初始化为一个正整数,并且只能递减。线程在等待阶段通过调用await()方法等待计数器变为0,而其他线程在完成自己的任务后通过调用countDown()方法来减少计数器的值。当计数器的值变为0时,所有等待的线程都将被唤醒继续执行。
CountDownLatch的主要方法包括:
void await():当前线程等待计数器的值变为0。如果计数器的值大于0,await()方法将导致线程阻塞,直到计数器的值变为0或被中断。
void countDown():将计数器的值减1。每个调用countDown()方法的线程都会使计数器减少1。
long getCount():获取当前计数器的值。
CountDownLatch通常用于以下场景:
启动多个线程等待某个任务的完成:主线程在创建需要等待的线程后,通过CountDownLatch的构造函数将计数器的值设置为等待的线程数。每个线程在完成任务后调用countDown()方法,计数器的值减1。主线程在需要等待的位置调用await()方法,直到计数器的值变为0,才会继续执行。
并行任务的等待和合并:多个线程同时执行某个任务,但是要求它们在继续执行前等待其他所有线程都完成。每个线程在完成自己的任务后调用countDown()方法,主线程调用await()方法等待所有线程完成。
测试并发性能:可以使用CountDownLatch来同时启动多个线程,然后在主线程中等待所有线程完成,以测试并发操作的性能。
通过使用CountDownLatch,我们可以实现线程之间的同步和协调,确保某些线程在其他线程完成任务后再继续执行,以及在需要等待多个线程完成后再进行下一步操作。
其中内部有一个类Sync可以看到该类继承AbstractQueuedSynchronizer,拥有AQS特性。
从构造函数来看Sync(int count),构造时传递一个线程状态存储。
//CountDownLatch的同步控制。使用AQS状态表示计数。private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {setState(count);}int getCount() {return getState();}protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}}
函数
await
-
await()函数是CountDownLatch的核心函数之一,用于使当前线程在计数器倒计数至零之前一直等待。如果线程被中断,则会抛出InterruptedException。
-
函数内部通过调用sync.acquireSharedInterruptibly(1)将操作转发给了Sync对象的acquireSharedInterruptibly方法。
public void await() throws InterruptedException {// 转发到sync对象上sync.acquireSharedInterruptibly(1);
}
acquireSharedInterruptibly
acquireSharedInterruptibly(int arg)方法在AQS中定义,用于在获取共享资源时可中断地等待。如果线程被中断,则会抛出InterruptedException。
acquireSharedInterruptibly(int arg)方法内部先检查线程的中断状态,如果被中断,则抛出InterruptedException。
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);
}
tryAcquireShared
如果通过tryAcquireShared(arg)方法尝试获取共享资源失败(返回值小于0),则调用doAcquireSharedInterruptibly(arg)方法进行进一步的等待操作。
tryAcquireShared(int acquires)是Sync类的方法,它根据AQS的状态来判断是否可以获取共享资源。在CountDownLatch的实现中,该方法简单地判断AQS的状态是否为0,如果为0,则返回1,表示可以获取共享资源;否则返回-1,表示不能获取共享资源
protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;
}
示例1
创建了一个CountDownLatch对象,并将其初始计数值设置为3(numOfThreads)。然后,我们创建了3个工作线程(Worker类的实例),每个工作线程模拟执行任务的时间。每个工作线程完成任务后,调用countDown()方法来减少计数器的值。
**主线程通过调用latch.await()来等待所有工作线程完成。**当计数器的值变为0时,主线程被唤醒,并打印出"All workers have completed their tasks."的消息。
该示例演示了如何使用CountDownLatch实现主线程等待多个工作线程完成任务后再继续执行的场景。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) {int numOfThreads = 3;CountDownLatch latch = new CountDownLatch(numOfThreads);for (int i = 0; i < numOfThreads; i++) {Thread thread = new Thread(new Worker(latch));thread.start();}try {latch.await(); // 主线程等待所有工作线程完成System.out.println("All workers have completed their tasks.");} catch (InterruptedException e) {e.printStackTrace();}}static class Worker implements Runnable {private final CountDownLatch latch;public Worker(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {// 模拟每个工作线程执行任务的时间try {Thread.sleep(2000);System.out.println("Worker completed its task.");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 每个工作线程完成任务后调用countDown()}}}
}
}
在上面的示例中,我们创建了一个CountDownLatch对象,并将其初始计数值设置为3(numOfThreads)。然后,我们创建了3个工作线程(Worker类的实例),每个工作线程模拟执行任务的时间。每个工作线程完成任务后,调用countDown()方法来减少计数器的值。
主线程通过调用latch.await()来等待所有工作线程完成。当计数器的值变为0时,主线程被唤醒,并打印出"All workers have completed their tasks."的消息。
该示例演示了如何使用CountDownLatch实现主线程等待多个工作线程完成任务后再继续执行的场景。
示例2
业务场景:某个业务操作非常耗时,但又必须等这个操作结束后才能进行后续操作。
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;/*** 多线程任务处理工具类*/
public class TaskDisposeUtils {//并行线程数public static final int POOL_SIZE;static {//判断核心线程数 如果机器的核心线程数大于5则用机器核心线程数POOL_SIZE = Integer.max(Runtime.getRuntime().availableProcessors(), 5);}/*** 并行处理,并等待结束**/public static <T> void dispose(List<T> taskList, Consumer<T> consumer) throws InterruptedException {dispose(true, POOL_SIZE, taskList, consumer);}/*** 并行处理,并等待结束**/public static <T> void dispose(boolean moreThread, int poolSize, List<T> taskList, Consumer<T> consumer) throws InterruptedException {if (CollectionUtils.isEmpty(taskList)) {return;}//如果是多线程且核心线程数大于一则进入方法if (moreThread && poolSize > 1) {poolSize = Math.min(poolSize, taskList.size());ExecutorService executorService = null;try {//新建一个固定大小的线程池 核心线程数为poolSizeexecutorService = Executors.newFixedThreadPool(poolSize);//juc工具类 用于让必须所有任务都处理完后才进行下一步CountDownLatch countDownLatch = new CountDownLatch(taskList.size());for (T item : taskList) {executorService.execute(() -> {try {//消费任务consumer.accept(item);} finally {//处理完后减一countDownLatch.countDown();}});}//在此等待 当countDownLatch变成0后才继续进行下一步countDownLatch.await();} finally {if (executorService != null) {executorService.shutdown();}}} else {for (T item : taskList) {consumer.accept(item);}}}public static void main(String[] args) throws InterruptedException {//生成1-10的10个数字,放在list中,相当于10个任务List<Integer> list = Stream.iterate(1, a -> a + 1).limit(10).collect(Collectors.toList());JSONObject object=new JSONObject();object.put("name","sss");//启动多线程处理list中的数据,每个任务休眠时间为list中的数值
// Consumer<Integer> c= item -> {
// try {
// long startTime = System.currentTimeMillis();
// object.put("s",item);
// TimeUnit.SECONDS.sleep(item);
// long endTime = System.currentTimeMillis();
// System.out.println(object.toJSONString());
// System.out.println(System.currentTimeMillis() + ",任务" + item + "执行完毕,耗时:" + (endTime - startTime));
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// };TaskDisposeUtils.dispose(list, item -> {try {long startTime = System.currentTimeMillis();object.put("s",item);TimeUnit.SECONDS.sleep(item);long endTime = System.currentTimeMillis();System.out.println(object.toJSONString());System.out.println(System.currentTimeMillis() + ",任务" + item + "执行完毕,耗时:" + (endTime - startTime));} catch (InterruptedException e) {e.printStackTrace();}});//上面所有任务处理完毕完毕之后,程序才能继续System.out.println(list + "中的任务都处理完毕!");}
}
相关文章:
Java - AQS-CountDownLatch实现类(二)
前言 在Java中,AbstractQueuedSynchronizer(简称AQS)是一个用于实现同步器的抽象类,它为实现各种类型的同步器(如锁、信号量等)提供了基本的框架。AQS通过一个双向队列(等待队列)和…...
rsut基础
这篇文章是实战性质的,也就是说原理部分较少,属于经验总结,rust对于模块的例子太少了。rust特性比较多(悲),本文的内容可能只是一部分,实现方式也不一定是这一种。 关于 rust 模块的相关内容&a…...
高压放大器和示波器的关系是什么
高压放大器和示波器是电子工程领域中常见的两种设备,它们在实际的电路设计、测试和分析中都扮演着重要的角色。下面安泰电子将从定义、功能、应用场景等方面为您介绍高压放大器和示波器的关系。 图:ATA-7000系列高压放大器 一、高压放大器的定义及功能 高…...
5个超实用视频素材网站,免费下载~
推荐几个高清无水印的视频素材网站,重点是可以免费下载使用,建议收藏! 菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky 可以称之为最大素材库,在这里你可以找到设计、办公、图片、视频、音频等各种素材。视频素材就有…...
【NLP模型】文本建模(1)(BoW、N-gram、tf-idf)
目录 一、说明 二、BoW模型产生发展 2.1 产生和历史 2.2 原理介绍 三、具体实现...
Java——网络编程套接字
目录 一、网络编程基础 1.1 为什么需要网络编程?——丰富的网络资源 二、什么是网络编程? 三、网络编程中的基本概念 3.2 请求和响应 3.3 客户端和服务端 常见的客户端服务端模型 四、Socket套接字 五、通信模型 5.1 Java数据报套接字通信模型 5.2 Java流…...
160套小程序源码
源码列表如下: AppleMusic (知乎日报) 微信小程序 d artand 今日更新求职招聘类 医药网 口碑外卖点餐 城市天气 外卖小程序 定位天气 家居在线 微信小程序-大好商城,wechat-weapp 微信小程序的掘金信息流 微信跳一跳小游戏源码 微票源码-demo 急救应急处…...
有效项目进度管理的 10 条规则
项目进度管理是项目中比较关键的方面之一,因为它将决定事情的进展方式、进展速度以及是否会取得进展。换句话说,它可以让你较好地控制项目,帮助你预测不可预测的情况,并使所有相关团队能够高效地协同工作。 以下是有效项目进度管…...
javaWebssh服装租赁店信息管理系统台myeclipse开发mysql数据库MVC模式java编程计算机网页设计
一、源码特点 java ssh服装租赁店信息管理系统是一套完善的web设计系统(系统采用ssh框架进行设计开发),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要 采用B/S模式开发。开发环境为TO…...
概率论:样本与总体分布,Z分数与概率
参考书目:《行为科学统计精要》(第八版)——弗雷德里克J格雷维特 数据及其样本的分布 描述一组数据分布 描述一组样本数据的分布 描述样本数据的均值和整体数据一样,但是样本标准差的公式除以了n-1,这里引入自由度的…...
【JavaSE】Java基础语法(十二):ArrayList
文章目录 1. ArrayList的构造方法和添加方法2. ArrayList类常用方法3. ArrayList存储学生对象并遍历 集合和数组的区别 : 共同点:都是存储数据的容器不同点:数组的容量是固定的,集合的容量是可变的 1. ArrayList的构造方法和添加方法 ArrayL…...
c++—封装:运算符重载、友元
1. 友元 (1)友元函数 ①是一种允许非类成员函数访问类的私有成员的一种机制;可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元; ②友元函数在类作用域外定义,但需要在类体中进行声明&…...
【K8s】安全认证与DashBoard
文章目录 一、概述1、客户端2、认证、鉴权与准入控制 二、认证管理1、认证方式2、HTTPS证书认证 三、授权管理1、授权与RBAC2、Role 与 ClusterRole3、RoleBinding 与 ClusterRoleBinding4、案例:创建一个只能管理dev空间下Pods资源的账号 四、准入控制五、DashBoar…...
SpringMVC第七阶段:SpringMVC的增删改查(01)
SpringMVC的增删改查 1、准备单表的数据库 drop database if exists springmvc;create database springmvc;use springmvc; ##创建图书表 create table t_book(id int(11) primary key auto_increment, ## 主键name varchar(50) not null, ## 书名 author varchar(50) no…...
接口测试-Mock测试方法
一、关于Mock测试 1、什么是Mock测试? Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取的比较复杂的对象(如 JDBC 中的ResultSet 对象&#…...
关于宝塔部署jar包和war包
文章目录 前言一、jar包部署二、war包部署1.maven如果打包不了使用命令打包2.安装Tomcat进行访问是否成功2.进入Tomcat目录进行配置war包 一、项目访问方法 前言 提示:以下是本篇文章正文内容,下面案例可供参考 一、jar包部署 1.其实jar包没什么讲的&…...
SpringMVC框架面试专题(初级-中级)-第十节
欢迎大家一起探讨~如果可以帮到大家请为我点赞关注哦~ 截止到本节关于SpringMVC的内容已经更新完毕,后续会更新SpringBoot框架的面试题;大家在背题的时候切记不要死记硬背,需要理解 这是什么?有什么操作&a…...
PCIe TLB事务层详解过程
目录 1.What is TLP 2.PCIe 4种不同的事务 2.1.Memory事务 2.2.IO事务 2.3.Configuration事务 2.4.Message...
RK3588平台开发系列讲解(项目篇)YOLOv5部署测试
平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、YOLOv5环境安装二、YOLOv5简单使用2.1、获取预训练权重文2.2、YOLOv5简单测试2.3、转换为rknn模型2.4、部署到 RK 板卡三、airockchip/yolov5简单测试3.1、转换成rknn模型并部署到板卡沉淀、分享、成长,让自己和他…...
基于变形模板的弱监督体图像分割
文章目录 Weakly Supervised Volumetric Image Segmentation with Deformed Templates摘要本文方法实验结果 Weakly Supervised Volumetric Image Segmentation with Deformed Templates 摘要 背景 有许多方法可以对网络进行弱监督训练来分割2D图像。依赖于对3D图像的2D切片的…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
