当前位置: 首页 > article >正文

Spring异步线程池的问题

今天看一视频,提到说 Spring默认的异步线程池比较简单,每次执行异步任务,都会新建一个线程进行处理,不会重复利用,所以在用Spring框架开发的时候,需要自定义异步线程池。第一次听到这个说法。遂开始百度。

百度 “Spring 默认的异步线程池的配置”

不到三秒看到了这么一个博客: https://www.cnblogs.com/zhaoyuan72/p/16347289.html

看到了 原文地址是从知乎来的,遂跳转到知乎:https://zhuanlan.zhihu.com/p/346086161。

我这个人有个毛病,干什么都想看到最原始的那个东西,比如听歌就要听原唱,不听改编翻唱,好吧,其实这样没必要。

作者开篇提到

上一篇分享了JDK自带的线程池 ThreadPoolExecutor 的配置和参数详解,然而我们实际开发中更多的是使用SpringBoot来开发,Spring默认也是自带了一个线程池方便我们开发,它就是ThreadPoolTaskExecutor,接下来我们就来聊聊Spring的线程池吧。

就是说 JDK自带的线程池是 ThreadPoolExecutor,而Spring框架又自我封装了一个线程池 ThreadPoolTaskExecutor。说实话,这个思想就是这样,JDK本身有自带的东西,很多框架觉得JDK的东西不好,都会自己二次改造,再来一个。

比如说JDK默认的序列化机制速度慢,生成的字节占用空间大,用的是java.io.ObjectInputStream 和 ObjectOutputStream。所以一般我们也不会用JDK的序列化机制,比如前后端交互我们用JSON,JSON又可以用FastJson或者Jackson等等。其他的场景如一个RPC框架我们可以用 Java 下面的最快的序列化工具 Kyro。(举例可能不恰当,但是就是这么个意思)

再比如说 Spring也有些东西,可能其他框架觉得它不太好,也会自己写一个。比如 StringUtils 这个类,一般开发中可能都会用 common.lang3的StringUtils,MP也会封装自己的 StringUtils,Hutool也会封装自己的StringUtils。

突然就理解了 common.lang 和 common.lang3 以及 fastjson 和fastjson2 的关系了。
commons.lang3 就是 common.lang 的3.x版本, 但是应该是一次大版本升级,所以重新搞成了一个artifact,fastjson2也是如此。
common.lang3的一个版本一定是从3.x开始的。fastjson2也一定是从2.x开始的

我们大致看一下 ThreadPoolExecutor。
参考了这篇文章。
https://blog.csdn.net/cy973071263/article/details/131484311 。

大致看完这个,开始看Spring的 ThreadPoolTaskExecutor。就看之前知乎的那一篇文章,作者先说的是 SimpleAsyncTaskExecutor,

在这里插入图片描述

在这里插入图片描述

于是我决定执行自己的demo

在这里插入图片描述

发现几个问题
1.没有如作者博客一样,我的demo线程复用了,没有一直创建,但是默认好像是8个线程
2.没有如博客作者一样打印出来 SimpleAsyncTaskExecutor 这个类

于是,去看了一下博客的日期,距今(2024年0925日)已经三年多了,Spring我目前用的都是5.3的版本了

在这里插入图片描述
看到了这个,可以去翻源码了,猜想是不是新版本Spring 默认的 concurrencyLimit 是8

在这里插入图片描述

看源码,全局搜 concurrencyLimit。

在这里插入图片描述
卧槽,那岂不是沙雕了。

又看一下我之前设置 的定时默认线程数是 8,但是我已经全部注释掉了呀
在这里插入图片描述

于是,开始去找新的博客看看。这次搜索的词条是:

Spring默认的异常线程池 SimpleAsyncTaskExecutor
并刻意去看2023、2024年的帖子,发现基本都说 默认的异步线程池 SimpleAsyncTaskExecutor 线程确实不可以重用。
感觉还是我本身项目代码的问题,我应该在某个地方设置了 最多可用线程数为8或者怎么样。

尝试1:既然注释没有效果,直接删掉 SchedulingConfig 这个类
发现还是一样的。

尝试2:去其他项目,运行这个demo
发现还是一样的 8 个线程在重用

则现在又有1种猜测
我加了 @Async 注解,到底有没有用 SimpleAsyncTaskExecutor 线程池

突然想起来了之前看 一个项目做的笔记。
说 @Async 注解的原理是

在这里插入图片描述
发现这里的 核心线程数确实是8 ,md破案了。
感觉根本没有用 SimpleAsyncTaskExecutor。

再去问 文心一言

当在Spring应用中使用@Async注解来声明异步方法时,如果没有明确指定线程池,那么默认情况下会使用SimpleAsyncTaskExecutor ,那如果是SpringBoot应用呢

在Spring Boot应用中,当使用@Async注解来声明异步方法时,如果没有明确指定线程池,其默认行为会根据Spring Boot的版本有所不同。

早期版本

在Spring Boot的某些早期版本中(通常指2.1之前的版本),如果没有特别配置,@Async注解标注的异步方法可能会默认使用SimpleAsyncTaskExecutor。这个执行器每次执行一个任务时都会创建一个新的线程,而不是复用现有的线程,这可能导致在高并发场景下创建大量线程,进而引发性能问题。

2.1及以后版本

从Spring Boot 2.1版本开始,情况发生了变化。在Spring Boot 2.1及以后的版本中,@Async注解默认使用的线程池不再是SimpleAsyncTaskExecutor,而是ThreadPoolTaskExecutor。这意味着,如果没有特别配置,异步方法将会在一个线程池中执行,该线程池具有核心线程数、最大线程数、队列容量等配置参数。

具体来说,ThreadPoolTaskExecutor的默认配置通常包括:

  • 核心线程数:8
  • 最大线程数Integer.MAX_VALUE
  • 队列容量Integer.MAX_VALUE(使用LinkedBlockingQueue
  • 空闲线程保留时间:60秒
  • 线程池拒绝策略AbortPolicy(当线程池饱和时,尝试执行新任务会抛出异常)

自定义配置

尽管Spring Boot提供了默认的线程池配置,但在实际应用中,根据业务需求和系统负载,这些默认配置可能需要调整。开发者可以通过在application.ymlapplication.properties文件中添加相应的配置属性来自定义线程池的行为。

例如,要调整核心线程数、最大线程数和队列容量,可以在application.properties中添加如下配置:

spring.task.execution.pool.core-size=10
spring.task.execution.pool.max-size=20
spring.task.execution.pool.queue-capacity=50

或者,在application.yml中添加类似配置:

spring:
  task:
    execution:
      pool:
        core-size: 10
        max-size: 20
        queue-capacity: 50

结论

因此,在Spring Boot应用中,如果没有明确指定线程池,@Async注解默认使用的线程池从Spring Boot 2.1版本开始是ThreadPoolTaskExecutor,而不是SimpleAsyncTaskExecutor。这提供了更好的线程复用和资源管理能力,但在实际应用中仍需要根据需要进行适当的配置调整。

再点进去

在这里插入图片描述

在这里插入图片描述

确实需要自定义异步线程池。

我之前的demo默认走的是 ThreadPoolTaskExecutor的配置。

但是这里我就好奇了,那在Spring 框架里面默认的线程池配置是通过哪个类实现的???

在这里插入图片描述

在这里插入图片描述

ai得出这个答案,我就不单独去验证Spring框架下面,@Scheduled 注解的行为了,毕竟现在都是基于SpringBoot开发。

如何自定义异步线程池,参考了 https://blog.csdn.net/kdzandlbj/article/details/139046266 的第二种方式。

在这里插入图片描述

异步线程池定义如图所示

在这里插入图片描述

启动项目,发现确实生效了,最大的执行线程就是5

在这里插入图片描述

但是这里有个问题,为什么 springBoot(这应该是SpringBoot决定的吧)的log打印,好像是打印 线程名.subtring(str.length-15)

在这里插入图片描述
稍等讨论这个问题,我想知道这个

executor.setThreadNamePrefix("AsyncThreadPool-t-");

线程工厂是如何把这个 线程名前缀加在线程上的.
首先要认知到一个问题,任何框架的线程池肯定都是基于JDK线程池的,而JDK线程池里面的线程都是由线程工厂创建的 也就是 ThreadFactory。所以,直接去找线程工厂。

在这里插入图片描述

在这里插入图片描述
点进去 CustomizableThreadFactory 看一看

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

再来说 SpringBoot输出的为什么是 substring的ThreadName。

在这里插入图片描述
找了一下感觉乱七八糟的,没什么章法,直接百度吧。

百度了一圈没有说什么源码的,都是说配置的,没劲,我自己debug看吧。

在这里插入图片描述

在这里插入图片描述

但是代码到下面就走完了。

在这里插入图片描述

事前先百度了一下。
在这里插入图片描述

ok,到这里 线程日志打印截断15个字符就结束了。


http://www.kler.cn/news/342714.html

相关文章:

  • 多模态大语言模型(MLLM)-Blip2深度解读
  • SQL 干货 | 使用 EXISTS 编写 SELECT 查询
  • LeetCode 3311. 构造符合图结构的二维矩阵
  • Pr 视频效果:元数据和时间码刻录 - 元数据
  • React学习01 jsx、组件与组件的三大属性
  • 每日一题:单例模式
  • 01数组算法/代码随想录
  • 【计算机网络】CDN
  • java多态-cnblog
  • 如何在Amazon EC2 Ubuntu安装vsftp并开启TLS
  • CSS3旋转、平移、缩放、倾斜
  • 云计算的江湖,风云再起
  • selenium的 presence_of_element_located 与 invisibility_of_element_located的区别
  • Android 编译使用哪个key签名?
  • 第二十篇:必知的网络常识概念
  • js操作元素的其他操作(4个案例+效果图+代码)
  • 【动态规划-4.2 最长递增子序列(LIS)】力扣300. 最长递增子序列
  • 基于C++ 实现简易图书管理系统
  • 01 为什么要学习数据结构与算法
  • 洗衣店订单管理:Spring Boot技术实现