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.rhkb.cn/news/374362.html

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

相关文章

单身杯_RE

唉&#xff0c;遇到几个比较繁琐的题目&#xff0c;搞的心态都有点炸了&#xff0c;0.0 magic 这题也就那样&#xff0c;初时想要用用 angr 跑了一下&#xff0c;没搞出来&#xff0c;之后再去好好搞清楚吧&#xff0c;也不是特别清楚运用。 然后就自己去看了&#xff0c;就是…

从实时监控到风险智能预警:EasyCVR视频AI智能监控技术在工业制造中的应用

随着科技的不断进步和工业制造领域的持续发展&#xff0c;传统的生产管理方式正逐渐转型&#xff0c;迈向更加智能、高效和安全的新阶段。在这个变革过程中&#xff0c;视频智能监控技术凭借其独特的优势&#xff0c;成为工业制造领域的管理新引擎&#xff0c;推动着从“制造”…

“删错文件后如何高效挽救?两大恢复策略全解析“

在数字化日益深入生活的今天&#xff0c;数据已成为我们工作、学习和娱乐不可或缺的一部分。然而&#xff0c;删错文件的经历却如同数字世界中的一场“小插曲”&#xff0c;不经意间就可能让我们陷入数据丢失的困境。无论是误触删除键、清空回收站&#xff0c;还是软件故障导致…

springboot中通过jwt令牌校验以及前端token请求头进行登录拦截实战

前言 大家从b站大学学习的项目侧重点好像都在基础功能的实现上&#xff0c;反而一个项目最根本的登录拦截请求接口都不会写&#xff0c;怎么拦截&#xff1f;为什么拦截&#xff1f;只知道用户登录时我后端会返回一个token&#xff0c;这个token是怎么生成的&#xff0c;我把它…

Matlab中collectPlaneWave函数的应用

查看文档如下&#xff1a; 可以看出最多5个参数&#xff0c;分别是阵列对象&#xff0c;信号幅度&#xff0c;入射角度&#xff0c;信号频率&#xff0c;光速。 在下面的代码中&#xff0c;我们先创建一个3阵元的阵列&#xff0c;位置为&#xff1a;&#xff08;-1,0,0&#x…

项目管理工具评测:2024年国内外最顶级的10款项目管理工具排行

国内外涌现出众多优秀的项目管理工具&#xff0c;它们各自在功能、易用性、集成能力等方面展现出独特优势。以下是国内外顶级的10款项目管理工具&#xff1a; 一、进度猫 推荐理由&#xff1a;进度猫以其直观的任务管理和进度跟踪功能&#xff0c;成为许多团队和项目的首选…

javaweb学习day4--《maven篇》maven的项目创建及其依赖管理详解(基于最新版本的idea)

一、前言 javaweb学习的第四天&#xff0c;不知道今天你们是否坚持下去了。今天学习到的是maven&#xff0c;温馨提示一下&#xff0c;idea中自带maven不用自行去下载了。前期的配置工作太过复杂了&#xff0c;小编感觉自己能力有限并不能将其讲的太清楚&#xff0c;还请大家在…

PlugLink的技术架构实例解析(附源码)

在探讨PlugLink这一开源应用的实际应用与技术细节时&#xff0c;我们可以从其构建的几个核心方面入手&#xff0c;结合当前AI编程的发展趋势&#xff0c;为您提供既有实例又有深度解析的内容。 PlugLink的技术架构实例解析 前端技术选型 —— layui框架&#xff1a; PlugLi…

maven——(重要)手动创建,构建项目

创建项目 手动按照maven层级建好文件夹&#xff0c;并写上java&#xff0c;测试代码和pom文件 构建项目 在dos窗口中执行如下命令 compile编译 当前maven仓库中什么都没有。 在pom所在层级下&#xff0c;执行&#xff1a; mvn compile 就开始显示下面这些&#xff0c;…

MQTT是什么,物联网

写文思路&#xff1a; 以下从几个方面介绍MQTT&#xff0c;包括&#xff1a;MQTT是什么&#xff0c;MQTT和webSocket的结合&#xff0c;以及使用场景&#xff0c; 一、MQTT是什么 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的发布/订阅消息…

JavaScript(7)——数组

JavaScript中数组的用法与Java差不多&#xff0c;但还是有一些区别 声明数组 语法: let 数组名 [数据1,数据2,数据...] let arr new Array(数据1,数据2,...数据n) 添加数据 数组.push()方法将一个或多个元素添加到数组末尾&#xff0c;并返回该数组新长度 <script>…

[k8s源码]1.client-go集群外部署

client-go是由k8s发布且维护的专门用于开发者和kubernetes交互的客户端库。它支持对k8s资源的CRUD操作&#xff08;create、read、update、delete&#xff09;&#xff0c;事件监听和处理&#xff0c;访问kubernetes集群的上下文和配置。 client go是独立于kubernetes集群之外…

SpringBoot项目架构实战之“网关zuul搭建“

第三章 网关zuul搭建 前言&#xff1a; 1、主要功能 zuul主要提供动态路由&#xff08;内置ribbon实现&#xff09;和过滤&#xff08;可以做统一鉴权过滤器、灰度发布过滤器、黑白名单IP过滤器、服务限流过滤器&#xff08;可以配合Sentinel实现&#xff09;&#xff09;功能…

css简单易懂的加载动画,看不会算我输好吧

效果展示 步骤 第一阶段 先准备结构&#xff0c;并且放置12个div&#xff0c;每一个div旋转30*n度&#xff0c; 做一个圆圈 dom <div class"modal"><div class"loading"><div class"item1"></div><div class&quo…

Vue 项目中 history 路由模式的使用

在最近帮客户开发的一个项目中&#xff0c;由于项目的特殊性&#xff0c;需要用到 Vue 中的 history路由模式。该模式使用时会涉及到“上传白屏”和“刷新 404 问题”。在帮助客户解决这两个问题的过程中&#xff0c;总结问题的解决方案并记录下来&#xff0c;希望能够保留这篇…

分布式训练

一、分布式计算 跟多GPU不同是&#xff1a;数据不是从主存拿的&#xff0c;是在分布式文件系统拿的&#xff0c;有多个工作站&#xff0c;工作站中有多个GPU&#xff0c;通过网络读取数据到GPU中&#xff0c;GPU通过网络接收到来自参数服务器的参数进行运算计算梯度&#xff0c…

怎样免费在线文字转语音?5个配音工具一键包揽

日常在享受有声读物的乐趣时&#xff0c;不知道大家是否也曾渴望将手中的精彩文本以生动的声音演绎出来&#xff1f; 无论是为了自我沉浸&#xff0c;还是为家人朋友创造独特的听觉盛宴&#xff0c;一款支持文本转语音的配音软件都能成为你的得力助手。它不仅能让文字跃然耳边…

【C++深度探索】全面解析多态性机制(一)

hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1…

演唱会售票系统(Springboot+MySQL+Mybatis+BootStrap)

本演唱会售票系统结合了多个流行的技术栈&#xff0c;提供了全面的功能模块&#xff0c;包括用户和管理员两个角色。前端采用Bootstrap框架设计响应式界面&#xff0c;后端采用Spring Boot和MyBatis Plus实现业务逻辑和数据库操作&#xff0c;Sa-Token确保系统的安全性。通过这…

深入分析与解决4.3问题:iOS应用版本更新审核被拒原因解析

深入分析与解决4.3问题&#xff1a;iOS应用版本更新审核被拒原因解析 在iOS应用开发和发布过程中&#xff0c;遇到4.3问题&#xff08;设计 - 垃圾邮件&#xff09;是一个常见且令人头疼的情况。即使您的应用已成功发布其第一个版本&#xff0c;但在进行版本更新时&#xff0c…