Spring SimpleAsyncTaskExecutor学习

一. 简介

  1. SimpleAsyncTaskExecutor,不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程,没有最大线程数设置;并发大的时候会产生严重的性能问题;
  2. 在 Java 中创建线程并不便宜,线程对象占用大量内存,在大型应用程序中,分配和取消分配许多线程对象会产生大量内存管理开销;
  3. 它会为每个人物启动一个新任务,异步执行它;
  4. 支持通过 concurrencyLimit 属性限制并发线程,也就是进行流控;默认情况下,不会进行流控,也就是说并发线程数是无限的;

目前了解的,Spring Kafka 的 KafkaListener 会使用 SimpleAsyncTaskExecutor,其他场景没有见使用;

二. 使用

1. 不进行并发限流

不进行并发限流,每次执行 SimpleAsyncTaskExecutor.execute(runnable) 都会创建一个新线程去异步执行任务;

/*** 不带并发限流控制的 SimpleAsyncTaskExecutor*/
public static void main(String[] args) {SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("my-test-");Runnable runnable = new Runnable() {@SneakyThrows@Overridepublic void run() {Thread.sleep(1000L);System.out.println("当前线程: " + Thread.currentThread().getName());}};for (int i = 0; i < 10; i++) {executor.execute(runnable);}
}

打印如下:

当前线程: my-test-4
当前线程: my-test-10
当前线程: my-test-5
当前线程: my-test-3
当前线程: my-test-9
当前线程: my-test-7
当前线程: my-test-2
当前线程: my-test-6
当前线程: my-test-8
当前线程: my-test-1

2. 进行并发限流

进行并发限流,每次执行 SimpleAsyncTaskExecutor.execute(runnable) 也会创建一个新线程去异步执行任务;

那并发限流和不并发限流的区别在哪呢?

  • 不并发限流:10 个线程并发执行;
  • 并发限流:concurrencyThrottle 并发线程设置为 3 的话,某一时刻只能有 3 个线程并发执行;
/*** 带并发限流控制的 SimpleAsyncTaskExecutor*/
public static void main(String[] args) {SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("my-test-");// 并发线程限制executor.setConcurrencyLimit(3);Runnable runnable = new Runnable() {@SneakyThrows@Overridepublic void run() {Thread.sleep(1000L);System.out.println("当前线程: " + Thread.currentThread().getName());}};for (int i = 0; i < 10; i++) {executor.execute(runnable);}// 会发现主线程被卡住,因为在 SimpleAsyncTaskExecutor 中会阻塞等待System.out.println("主线程执行完成");
}

打印如下:

当前线程: my-test-3
当前线程: my-test-1
当前线程: my-test-2
------------------------------------------
当前线程: my-test-6
当前线程: my-test-5
当前线程: my-test-4
------------------------------------------
当前线程: my-test-8
当前线程: my-test-7
当前线程: my-test-9
------------------------------------------
主线程执行完成
当前线程: my-test-10

三. 源码分析

SimpleAsyncTaskExecutor 的源码比较少,我们直接看这个类;

public class SimpleAsyncTaskExecutor extends CustomizableThreadCreatorimplements AsyncListenableTaskExecutor, Serializable {/*** -1 表示不进行并发限流*/public static final int UNBOUNDED_CONCURRENCY = -1;/*** 0 表示其他线程等待其他线程执行完*/public static final int NO_CONCURRENCY = 0;// 并发限流的实现对象// 并发限流就是靠这个类实现的private final ConcurrencyThrottleAdapter concurrencyThrottle = new ConcurrencyThrottleAdapter();@Nullableprivate ThreadFactory threadFactory;@Nullableprivate TaskDecorator taskDecorator;public SimpleAsyncTaskExecutor() {super();}public SimpleAsyncTaskExecutor(String threadNamePrefix) {super(threadNamePrefix);}/*** 设置并发线程数* 给 concurrencyThrottle 的 concurrencyLimit 字段设值* 默认 concurrencyThrottle 的 concurrencyLimit 的值为 -1,表示不进行并发限流*/public void setConcurrencyLimit(int concurrencyLimit) {this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);}/*** 当前是否是并发限流的* 其实就是看 concurrencyThrottle 的 concurrencyLimit >= 0 ?*/public final boolean isThrottleActive() {return this.concurrencyThrottle.isThrottleActive();}/*** 常用的 execute()*/@SuppressWarnings("deprecation")@Overridepublic void execute(Runnable task) {execute(task, TIMEOUT_INDEFINITE);}/*** 执行给定的 task*/@Deprecated@Overridepublic void execute(Runnable task, long startTimeout) {Assert.notNull(task, "Runnable must not be null");Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);// 1. 如果需要进行并发限流,走下面的逻辑if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {this.concurrencyThrottle.beforeAccess();doExecute(new ConcurrencyThrottlingRunnable(taskToUse));}else {// 2. 不需要并发限流,直接执行 doExecute(task)doExecute(taskToUse);}}protected void doExecute(Runnable task) {// 1. 直接创建一个新的线程!!!// 这就是为什么 SimpleAsyncTaskExecutor 每次执行 execute() 都会创建一个新线程的原因Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));// 2. 调用 thread.start() 异步执行任务thread.start();}/*** ConcurrencyThrottleAdapter 只有两个方法,都是由父类实现的*/private static class ConcurrencyThrottleAdapter extends ConcurrencyThrottleSupport {@Overrideprotected void beforeAccess() {super.beforeAccess();}@Overrideprotected void afterAccess() {super.afterAccess();}}/*** 包装了 Runnable 对象,并且本身也是 Runnable 对象* 装饰器模式*/private class ConcurrencyThrottlingRunnable implements Runnable {private final Runnable target;public ConcurrencyThrottlingRunnable(Runnable target) {this.target = target;}@Overridepublic void run() {try {this.target.run();}finally {// 主要是在 finally 块中执行 concurrencyThrottle.afterAccess()concurrencyThrottle.afterAccess();}}}}

通过上面的描述,我们对 SimpleAsyncTaskExecutor 有了简单的认识;

不进行并发流控的情况下,很好理解,每次执行 SimpleAsyncTaskExecutor.execute() 都会创建一个新的线程;

我们主要看下进行并发流控的情况下,它是怎么进行流控的;

// ---------------------- SimpleAsyncTaskExecutor ------------------------
public void execute(Runnable task, long startTimeout) {Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);// 1. 如果需要进行并发限流,走下面的逻辑if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {// 1.1 执行 this.concurrencyThrottle.beforeAccess()// 如果被并发限流的话会阻塞等待this.concurrencyThrottle.beforeAccess();// 1.2 此时没有被限流住// 将 task 包装为 ConcurrencyThrottlingRunnable// ConcurrencyThrottlingRunnable 的 run() 的 finally 块会释放资源// 使其他线程能通过限流doExecute(new ConcurrencyThrottlingRunnable(taskToUse));}else {// 2. 不需要并发限流,直接执行 doExecute(task)doExecute(taskToUse);}
}

this.concurrencyThrottle.beforeAccess() 和 ConcurrencyThrottlingRunnable.run() 都是关键点,下面我们分开分析;

1. concurrencyThrottle.beforeAccess()

可以看到它是通过 synchronized 和 concurrencyLimit 来控制并发限流的;

// ---------------------- ConcurrencyThrottleSupport ------------------------
protected void beforeAccess() {if (this.concurrencyLimit == 0) {// 不允许 concurrencyLimit == 0throw new IllegalStateException();}// 1. 存在并发限流的场景,this.concurrencyLimit > 0if (this.concurrencyLimit > 0) {// 2. 尝试获取 monitor 对象锁,获取不到的话在这里阻塞,等其他线程释放锁synchronized (this.monitor) {// 3. 如果当前并发线程 >= this.concurrencyLimit// 当前线程 wait 等待,直到其他线程唤醒它while (this.concurrencyCount >= this.concurrencyLimit) {this.monitor.wait();}// 4. 当前并发线程数 concurrencyCount++this.concurrencyCount++;}}
}

2. ConcurrencyThrottlingRunnable

我们看下这个类的 run();

// --------------------- ConcurrencyThrottlingRunnable -----------------------
private class ConcurrencyThrottlingRunnable implements Runnable {private final Runnable target;public ConcurrencyThrottlingRunnable(Runnable target) {this.target = target;}@Overridepublic void run() {try {// 1. 执行目标 target.run()this.target.run();}finally {// 2. 执行 concurrencyThrottle.afterAccess()concurrencyThrottle.afterAccess();}}
}// ---------------------- ConcurrencyThrottleSupport ------------------------
protected void afterAccess() {// 并发限流场景下// 先获取 monitor 对象锁,执行 concurrencyCount--,再唤醒 wait 中的线程if (this.concurrencyLimit >= 0) {synchronized (this.monitor) {this.concurrencyCount--;this.monitor.notify();}}
}

至此,SimpleAsyncTaskExecutor 分析完毕;

实际开发中,我们不要使用 SimpleAsyncTaskExecutor,避免发生灾难性的问题;

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

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

相关文章

C#——封装详情

C# 封装 封装是面向对象编程中的一个核心概念&#xff0c;它使得对象的状态&#xff08;即数据&#xff09;不会被直接访问&#xff0c;而是通过方法&#xff08;即行为&#xff09;来控制和操作。封装可以提高代码的内聚性和可维护性&#xff0c;同时也提供了一种保护数据不被…

ARM汇编与机器码、汇编指令

文章目录 1. CISC与RISC指令集 2. ARM汇编指令 3. 汇编与机器码 4. 汇编指令格式 5. MOV指令 6. BL指令 7. B指令 8. ADD/SUB指令 9. LDR/STR指令 1. CISC与RISC指令集 根据指令的复杂度&#xff0c;所有CPU可以分为两类&#xff1a; CISC&#xff08;Complex Instr…

C#——多态详情

多态 多态: 是同一个行为&#xff0c;具有多个不同表现形式或形态的能力 多态分为两种 : 静态性多态: 函数重载&#xff0c;符号重载动态性多态: 虚方法&#xff0c;抽象类&#xff0c;接口 静态多态 在编译时&#xff0c;函数和对象的连接机制被称为早期绑定&#xff0c;…

知识图谱的符号表示

1、基于图的表示建模 2、有效标记图 3、属性图表示方法的优点和去点 4、RDF是什么&#xff1f;表达是有限的 5、OWL&#xff0c;本体语言 6、OWL有很多家族 7、属性图、RDF\三元组、关系图 ---------------------------PPT---------------------

三级_网络技术_07_IP地址规划技术

1.IP地址192.168.15.1/27的子网掩码可写为()。 255.255.255.192 255.255.255.224 255.255.255.240 255.255.255.248 2.IP地址块211.64.0.0/11的子网掩码可写为()。 255.192.0.0 255.224.0.0 255.240.0.0 255.248.0.0 3.IP地址块59.67.159.125/11的子网掩码可写为()。…

STM32与W25Q64 Flash:SPI通信协议的高效实现策略

摘要 在嵌入式系统中&#xff0c;SPI通信协议是实现微控制器与非易失性存储设备如W25Q64 Flash存储器之间数据交换的关键技术。本文将探讨STM32微控制器与W25Q64 Flash存储器通过SPI进行通信的高效实现策略&#xff0c;包括硬件配置、SPI接口编程、性能优化技巧以及实际代码示…

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法,亲测有效!!!

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 一、问题分析 二、报错原因 三、解决思路 四、解决方法 五、总结 博主v&#xff1a;XiaoMing_Java 博主v&#x…

【微服务网关——服务发现】

1.服务发现 1.1 介绍 服务发现是指用注册中心来记录服务信息&#xff0c;以便其他服务快速查找已注册服务服务发现分类: 客户端服务发现服务端服务发现 1.2 客户端服务发现 客户端服务发现&#xff08;Client-side Service Discovery&#xff09;是一种微服务架构中的模式…

Vine: 一种全新定义 Vue 函数式组件的解决方案

7月6日的 vue confg 大会上 ShenQingchuan 大佬介绍了他的 Vue Vine 项目&#xff0c; 一种全新定义 Vue 函数式组件的解决方案。 和 React 的函数式组件有异曲同工之妙&#xff0c;写起来直接起飞了。 让我们来快速体验一下 vine&#xff0c; 看看到底给我们带来了哪些惊喜吧…

释放计算潜能:Mojo模型与分布式训练的融合之道

释放计算潜能&#xff1a;Mojo模型与分布式训练的融合之道 在当今数据驱动的世界中&#xff0c;机器学习模型常常需要处理庞大的数据集&#xff0c;并且模型的复杂性也在不断增加。这导致训练模型所需的计算资源和时间显著增长。分布式训练作为一种有效的解决方案&#xff0c;…

[Python]配置邮件服务,发送邮件

本文以163邮件系统为例&#xff0c;登录之后&#xff0c;点击设置&#xff0c;开启如下设置项。 即可使用代码发送邮件&#xff0c;并携带附件。 开启SMTP 普通邮件 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart# 1…

文心一言常用的指令

文心一言作为一款强大的人工智能模型&#xff0c;支持多种类型的指令&#xff0c;以满足用户的不同需求。以下是一些文心一言常用的指令类型及其具体示例&#xff0c;这些指令按照不同的功能和用途进行分类和归纳&#xff1a; 1. 查询类指令 知识问答&#xff1a; 指令示例&a…

[算法] 优先算法(四):滑动窗口(下)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

python 性能提升-C扩展

文章目录 python的缺点性能提升C扩展案例python的缺点 运行效率低 性能提升 性能瓶颈问题使用如下方式解决: C/C++实现耗时的代码,然后使用gcc/g++编译为动态库dll/so,在python中使用ctypes模块进行加载动态库;C为python耗时的代码编写扩展,需使用 Python.h 头文件,…

20240708 每日AI必读资讯

&#x1f916;破解ChatGPT惊人耗电&#xff01;DeepMind新算法训练提效13倍&#xff0c;能耗暴降10倍 - 谷歌DeepMind研究团队提出了一种加快AI训练的新方法——多模态对比学习与联合示例选择&#xff08;JEST&#xff09;&#xff0c;大大减少了所需的计算资源和时间。 - JE…

Java线程的创建·启动和休眠

一.线程的创建和启动 Java中创建线程的两种方式 ◆继承java.lang.Thread类 ◆实现java.lang.Runnable接口 ◆使用线程的步骤 继承Thread类创建线程 ◆自定义线程类继承自Thread类 ◆重写run()方法&#xff0c;编写线程执行体 ◆创建线程对象&#xff0c;调用start()方法启动…

目标检测YOLO实战应用案例100讲-基于深度学习的无人机影像小目标识别(续)

目录 3.2 实验平台和环境 3.3 实验评价指标 3.4 基础框架YOLOv5在无人机数据集上的实验 3.4.1 实验结果 3.4.2 结果分析 4基于深度学习的无人机影像目标检测算法 4.1 基于改进YOLOv5的小目标检测算法研究 4.1.1 增加注意力机制 4.1.2 增加检测层 4.1.3多尺…

2024年 春秋杯 网络安全联赛夏季赛 Web方向 题解WirteUp 部分

brother 题目描述&#xff1a;web哥&#xff0c;打点容易提权难。 打点就是最简单的SSTI。 执行下find / -user root -perm -4000 -print 2>/dev/null找一下具备suid权限的命令 /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/bin/chsh /usr/bin/gpasswd /usr/bin/n…

互联网十万个为什么之什么是数据备份?

数据备份是按照一定的备份频率创建数据副本的过程&#xff0c;将重要的数据复制到其它位置或者存储介质&#xff0c;并对生成的副本保留一定的时长。备份通常储存在不同的物理介质或云端&#xff0c;以确保数据的连续性和完整性。有效的备份策略至关重要&#xff0c;以防止数据…

macos 10.15系统下载包,macOS Catalina for mac

macOS Catalina 让你喜欢的种种 Mac 体验都更进一步。你可以领略音乐、播客这两款全新 Mac app 的表演&#xff1b;在 Mac 上畅享各款自己心爱的 iPad app&#xff1b;拿起 iPad 和 Apple Pencil&#xff0c;拓展工作空间&#xff0c;释放创意灵感&#xff1b;再打开那些平时常…