@Async使用什么线程池?

news2024/11/25 16:55:26

文章目录

  • 前言
  • 一、前言
    • 1、ThreadPoolTaskExecutor
    • 2、SimpleAsyncTaskExecutor
    • 3、测试代码
  • 二、各种情况模拟
    • 1、未配置线程池
    • 2、配置异步线程池
    • 3、配置1个或多个非异步线程池
    • 4、同时配置异步和非异步线程池
  • 三、总结

前言

本文的目的,主要是看到网上各种说辞,抄来抄去,说异步方法不配置线程池会出现大问题等等,通过实验,来证明不同情况下,执行异步方法,使用的线程来自于什么线程池,来纠正大家以往的认知

一、前言

1、ThreadPoolTaskExecutor

源码如下,核心线程数为1,最大线程数和队列容量为Integer.MAX_VALUE

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {

	private final Object poolSizeMonitor = new Object();

	private int corePoolSize = 1;

	private int maxPoolSize = Integer.MAX_VALUE;

	private int keepAliveSeconds = 60;

	private int queueCapacity = Integer.MAX_VALUE;

	private boolean allowCoreThreadTimeOut = false;

	...
}

2、SimpleAsyncTaskExecutor

/**
 * {@link TaskExecutor} implementation that fires up a new Thread for each task,
 * executing it asynchronously.
 *
 * <p>Supports limiting concurrent threads through the "concurrencyLimit"
 * bean property. By default, the number of concurrent threads is unlimited.
 *
 * <p><b>NOTE: This implementation does not reuse threads!</b> Consider a
 * thread-pooling TaskExecutor implementation instead, in particular for
 * executing a large number of short-lived tasks.
 */
@SuppressWarnings("serial")
public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
		implements AsyncListenableTaskExecutor, Serializable {
	....
}

以上是部分源码,大概意思就是会为每个任务启动一个新线程,异步执行它,可以通过 “concurrencyLimit” bean属性限制并发线程,默认情况下,并发线程数不受限制,比较适用于执行大量短期任务

3、测试代码

我们通过快速请求12次以下方法来看看执行情况

@Async
@Override
public void doAsyc() throws InterruptedException {
	System.out.println("我是异步方法,线程名:" + Thread.currentThread().getName());
	Thread.sleep(1000);
}

二、各种情况模拟

我们通过模拟项目中未配置线程池,配置了异步线程池,配置了非异步线程池,以及两种线程池组合来分析下

1、未配置线程池

运行结果如下:

我是异步方法,线程名:task-4
我是异步方法,线程名:task-5
我是异步方法,线程名:task-6
我是异步方法,线程名:task-3
我是异步方法,线程名:task-2
我是异步方法,线程名:task-1
我是异步方法,线程名:task-8
我是异步方法,线程名:task-7
我是异步方法,线程名:task-2
我是异步方法,线程名:task-4
我是异步方法,线程名:task-6
我是异步方法,线程名:task-5

通过上面结果,看不出来到底是使用哪个线程池?我们通过打断点来看看
在这里插入图片描述
通过debug,可以发现使用的ThreadPoolTaskExecutor线程池,corePoolSize=8,maxPoolSize、queueCapacity=Integer.MAX_VALUE

2、配置异步线程池

异步线程池

@Configuration
@EnableAsync
public class ForlanAsyncConfig implements AsyncConfigurer {

	private int corePoolSize = 3;
	private int maxPoolSize = 3;
	private int queueCapacity = 10;

	@Bean
	@Override
	public Executor getAsyncExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(corePoolSize);
		executor.setMaxPoolSize(maxPoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setThreadNamePrefix("forlan-async-thread-pool");
		executor.initialize();
		return TtlExecutors.getTtlExecutor(executor);
	}
}

运行结果如下:

我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1

通过debug,发现使用的是我们自定义的异步线程池
在这里插入图片描述

3、配置1个或多个非异步线程池

非异步线程池1

@Configuration
@EnableAsync
public class Forlan1ThreadPoolConfig {

	private int corePoolSize = 1;
	private int maxPoolSize = 1;
	private int queueCapacity = 10;

	@Bean
	public Executor forlan1ThreadPool() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(corePoolSize);
		executor.setMaxPoolSize(maxPoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setThreadNamePrefix("forlan1-thread-pool");
		executor.initialize();
		return TtlExecutors.getTtlExecutor(executor);
	}
}

非异步线程池2

@Configuration
@EnableAsync
public class Forlan2ThreadPoolConfig {

	private int corePoolSize = 2;
	private int maxPoolSize = 2;
	private int queueCapacity = 10;

	@Bean
	public Executor forla2ThreadPool() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(corePoolSize);
		executor.setMaxPoolSize(maxPoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setThreadNamePrefix("forlan2-thread-pool");
		executor.initialize();
		return TtlExecutors.getTtlExecutor(executor);
	}
}

运行结果如下:运行了12个任务,创建了12个线程执行

我是异步方法,线程名:SimpleAsyncTaskExecutor-1
我是异步方法,线程名:SimpleAsyncTaskExecutor-3
我是异步方法,线程名:SimpleAsyncTaskExecutor-2
我是异步方法,线程名:SimpleAsyncTaskExecutor-4
我是异步方法,线程名:SimpleAsyncTaskExecutor-6
我是异步方法,线程名:SimpleAsyncTaskExecutor-7
我是异步方法,线程名:SimpleAsyncTaskExecutor-5
我是异步方法,线程名:SimpleAsyncTaskExecutor-8
我是异步方法,线程名:SimpleAsyncTaskExecutor-9
我是异步方法,线程名:SimpleAsyncTaskExecutor-11
我是异步方法,线程名:SimpleAsyncTaskExecutor-10
我是异步方法,线程名:SimpleAsyncTaskExecutor-12

通过debug,发现使用的SimpleAsyncTaskExecutor线程池,就是来一个任务创建一个线程
在这里插入图片描述

4、同时配置异步和非异步线程池

运行结果如下:

我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool1
我是异步方法,线程名:forlan-async-thread-pool2
我是异步方法,线程名:forlan-async-thread-pool3
我是异步方法,线程名:forlan-async-thread-pool1

通过debug,发现使用的是我们自定义的异步线程池
在这里插入图片描述

三、总结

  • 未配置线程池,使用的是默认的ThreadPoolTaskExecutor,corePoolSize=8,maxPoolSize、queueCapacity=Integer.MAX_VALUE
  • 配置异步线程池:使用的是我们自定义的异步线程池
  • 配置1个或多个非异步线程池:使用的是SimpleAsyncTaskExecutor,来一个任务创建一个新线程执行
  • 同时配置异步和非异步线程池:使用的是我们自定义的异步线程池

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

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

相关文章

JVM-类加载与运行区详细分析(一)

目录 一、为什么会有类加载机制 二、类加载机制原理是什么 1、什么是类加载器&#xff1a;宏观 2、类加载器工作原理 1、装载 2、链接 3、初始化 3、何为装载的机制&#xff1a;微观 4、上面既然我们已经知道了啥是双亲委派了&#xff0c;那么怎么去破坏呢&#xff1f;…

【设计模式】工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)详记

注&#xff1a;本文仅供学习参考&#xff0c;如有错漏还请指正&#xff01; 参考文献/文章地址&#xff1a; https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B…

第20章:MySQL索引失效案例

1.全值匹配我最爱 当SQL查询 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age30 AND classId4 AND NAMEabcd; 创建3个索引 idx_age,idx_age_classid,idx_age_classid_name 当前优化器会选择跟where条件匹配最高的idx_age_classid_name索引&#xff0c;直接查询出对…

[CVPR 2023] Imagic:使用扩散模型进行基于文本的真实图像编辑

[CVPR 2023] Imagic:使用扩散模型进行基于文本的真实图像编辑 Paper Title: Imagic: Text-Based Real Image Editing with Diffusion Models The first author performed this work as an intern at Google Research. Project page: https://imagic-editing.github.io/. 原文…

登出成功后token过期方案

目录 需求分析解决方案实现步骤登出成功相关逻辑改造携带token请求相关逻辑需求分析 登录成功后,系统会返回一个token给客户端使用,token可以用来获取登录后的一些资源或者进行一些操作。当用户在系统中注销或者退出登录时,需要对token进行过期处理,以保证系统的安全性和数…

校园网WiFi IPv6免流上网

ipv6的介绍 IPv6是国际协议的最新版本&#xff0c;用它来取代IPv4主要是为了解决IPv4网络地址枯竭的问题&#xff0c;也在其他很多方面对IPv4有所改进&#xff0c;比如网络的速度和安全性。 IPv4是一个32位的地址&#xff0c;随着用户的增加在2011年国家报道说IPv4的网络地址即…

基于前后端交互的论坛系统(课设高分必过)

目录 前言概述 一.前期准备 项目演示 用户注册 用户登录 主页面 发帖页面 个人信息及修改 用户主帖 站内信 需求分析 技术选型 二.建表分析 三.环境搭建 技术环境的检查与安装 检查JDK ​编辑 检查数据库 检查Maven 检查git ​编辑 项目启动的通用配置 新…

【系统架构】第五章-软件工程基础知识(需求工程和系统分析与设计)

软考-系统架构设计师知识点提炼-系统架构设计师教程&#xff08;第2版&#xff09; 需求工程 软件需求3个层次&#xff1a; 业务需求&#xff1a;反映了组织机构或客户对系统、产品高层次的目标要求用户需求&#xff1a;描述了用户使用产品必须要完成的任务&#xff0c;是用户…

rust abc(3): 布尔和字符类型的使用并与C/C++对比

文章目录 1. 目的2. 布尔类型2.1 只能赋值为小写的 true, false2.2 不能把数字赋值给bool类型变量2.3 正确写法汇总 3. 字符类型3.1 UTF-8 编码3.2 字符的意思是单个字符&#xff0c;多个字符不能用单引号 4. 总结 1. 目的 继续熟悉 rust 语言的基本数据类型, 感受 rust 编译期…

Redis【实战篇】---- 短信登录

Redis【实战篇】---- 短信登录 1. 导入黑马点评项目1. 导入SQL2. 有关当前模型3. 导入后端项目4. 导入前端项目5. 运行前端项目 2. 基于Session实现登录流程3. 实现发送短信验证码功能4. 实现登录拦截功能5. 隐藏用户敏感信息6. session共享问题7. Redis代替session业务1. 设计…

Spark10-11

10. 广播变量 10.1 广播变量的使用场景 在很多计算场景&#xff0c;经常会遇到两个RDD进行JOIN&#xff0c;如果一个RDD对应的数据比较大&#xff0c;一个RDD对应的数据比较小&#xff0c;如果使用JOIN&#xff0c;那么会shuffle&#xff0c;导致效率变低。广播变量就是将相对…

【C/C++】使用类和对象 练习EasyX图形库

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

【关联式容器】之map和set

【关联式容器】之map和set 容器类型树形结构的关联式容器mapset&#xff0c;multiset&#xff0c;multimap的区别与联系 容器类型 在STL中&#xff0c;我们接触过许多容器&#xff0c;例如&#xff1a;vector&#xff0c;list&#xff0c;stack&#xff0c;queue&#xff0c;m…

conll2003数据集下载与预处理

CoNLL-2003 数据集包括 1,393 篇英文新闻文章和 909 篇德文新闻文章。我们将查看英文数据。 1. 下载CoNLL-2003数据集 https://data.deepai.org/conll2003.zip 下载后解压你会发现有如下文件。 打开train.txt文件&#xff0c; 你会发现如下内容。 CoNLL-2003 数据文件包含由单…

逍遥自在学C语言 | 指针陷阱-空指针与野指针

前言 在C语言中&#xff0c;指针是一种非常强大和灵活的工具&#xff0c;但同时也容易引发一些问题&#xff0c;其中包括空指针和野指针。 本文将带你了解这两个概念的含义、产生原因以及如何避免它们所导致的问题。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直…

【玩转Docker小鲸鱼叭】理解DockerFile如此简单

DockerFile构建过程 DockerFile 是Docker的一个配置文件&#xff0c;本质上来说它只是一个文本文件&#xff0c;它是用来构建Docker镜像的。DockerFile配置文件中包含了一系列的指令和配置信息&#xff0c;用于描述如何构建镜像以及如何运行容器。通过编写 Dockerfile&#xf…

RISC-V处理器的设计与实现(二)—— CPU框架设计

前面我们选好了要实现的指令集&#xff0c;并且了解了每个指令的功能&#xff08;传送门&#xff1a;RISC-V处理器的设计与实现&#xff08;一&#xff09;—— 基本指令集_Patarw_Li的博客-CSDN博客&#xff09;&#xff0c;接下来我们就可以开始设计cpu了。当然我们不可能一上…

ChatGPT更新的使用指南,与其他类似的人工智能的软件和服务-更新版(2023-6-25)

文章目录 一、什么是ChatGPT二、如何使用三、如何使用ChatGPT帮助我们的工作和生活四、高阶用法1、角色扮演2、英语口语老师3、在搜索引擎中集成ChatGPT 五、常见问题五、其他类似的软件和服务 如果你还不知道如何注册和使用&#xff0c;可看末尾&#xff0c;手把手教你。 一、…

Linux线程同步

同步的几种方式&#xff1a;信号量&#xff0c;互斥锁&#xff0c;条件变量&#xff0c;读写锁 同步&#xff1a;对程序的执行过程进行控制&#xff0c;保证对临界资源的访问同一时刻只能有一个进程或线程访问。 2.1信号量 存在P操作&#xff1a;获取资源&#xff0c;信号量…

58.最后一个单词的长度

LeetCode-58.最后一个单词的长度 1、题目描述2、解题思路3、代码实现4、解题记录 1、题目描述 题目描述&#xff1a; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任…