线程池系列文章可参考下表,目前已更新完毕…
线程池系列:
文章
Java基础线程池
深入剖析Java线程池的核心概念与源码解析:从Executors、Executor、execute逐一揭秘
CompletableFuture线程池
从用法到源码再到应用场景:全方位了解CompletableFuture及其线程池
SpringBoot默认线程池(@Async和ThreadPoolTaskExecutor)
探秘SpringBoot默认线程池:了解其运行原理与工作方式(@Async和ThreadPoolTaskExecutor)
SpringBoot默认线程池和内置Tomcat线程池
你是否傻傻分不清SpringBoot默认线程池和内置Tomcat线程池?
在Java应用程序中,线程池是一种用于管理和重用线程的机制。线程池可以显著提高多线程应用程序的性能,避免不必要的线程创建和销毁开销,同时有效控制并发线程数量,防止系统资源被耗尽。
对于SpringBoot程序,我们知道它是会有一个内置的Tomcat,但是我自己之前一直对于SpringBoot默认线程池和SpringBoot内置Tomcat线程池概念不是很清晰,很容易混淆,甚至以为它们就是一个东西,经过这次深入了解以后,发现两个完全是不同的东西,接下来跟我一起探索吧~
在Spring Boot应用中,涉及到两种常见的线程池:Spring Boot内部使用的异步任务执行的线程池(通常指的是通过注解定义的异步任务,或者是直接注入自定义的线程池进行使用),以及内嵌Tomcat容器用于处理HTTP请求的线程池。它们的主要区别在于用途、配置方式和管理策略。
这里用一张图帮助理解,Spring Boot默认线程池主要处理应用程序中的后台任务,而Tomcat线程池专门处理Web层面的HTTP请求。
默认配置下,连接超过10000后会出现拒绝连接情况
默认配置下,触发的请求超过200+100后拒绝处理(最大工作线程数+等待队列长度)
默认设置中,Tomcat的最大线程数是200,最大连接数是10000。支持的并发量是指连接数,200个线程如何处理10000条连接的?
目前Tomcat有三种处理连接的模式,一种是BIO,一个线程只处理一个连接,另一种就是NIO,一个线程处理多个连接。由于HTTP请求不会太耗时,而且多个连接一般不会同时来消息,所以一个线程处理多个连接没有太大问题。还有一种是apr模式,这里不做深入讨论。
Tomcat启动的时候,可以通过log看到Connector使用的是哪一种运行模式:我们可以发现就是NIO模式
Tomcat创建线程池的时候底层还是利用JDK的ThreadPoolExecutor,所以在这个地方打个断点即可!
springboot启动的时候,底层还是依赖于spring的,所以会调用org.springframework.context.support.AbstractApplicationContext#refresh
在这里之后的流程如下,最终会启动org.springframework.boot.web.embedded.tomcat.TomcatWebServer#start,接着就会创建对应的线程池了,这里最终也是用JDK里面的线程池创建的!
这些默认的配置我们当然也可以自定义重新配置过,如下所示,我们来看看不同线程情况下,对应的请求流程是怎么样的!
网上说的配置参数(仅供参考):
线程数的经验值为:1核2G内存,线程数经验值200;4核8G内存, 线程数经验值800。
(4核8G内存单进程调度线程数800-1000,超过这个并发数之后,将会花费巨大的时间在CPU调度上)
根据提供的Tomcat参数配置,我们可以分析在不同线程数下的请求处理流程情况:
下面根据不同的情况给出对应的请求处理流程:
适当调整这些参数,可以确保Tomcat根据可用资源和预期负载以最优方式处理请求。然而,合理的参数配置需要根据实际运行环境和需求来进行调整,并且在实际部署时进行持续观察和调优。
和 是Tomcat配置中两个重要但概念上不同的参数。理解这两个参数及其区别对于优化Tomcat性能和容量规划非常关键。
从本质上说, 更多地涉及到服务器对外提供服务的网络连接的能力,而 则是处理资源(如线程)饱和时的缓冲能力。前者是决定服务能够接收多少并发连接的网络层面的限制,后者则是应用层面上关于当处理瓶颈出现时如何管理新的入站连接的策略。
将上述例子结合起来考虑,假如同时有100个请求向服务器发起连接,根据,这些连接都可以成功建立。但是,因为设置为20,所以只有20个请求能够同时被处理,剩下的请求里,最多有10个能够进入等待队列(按照定义),而超过这30(20+10)的部分请求则将因为无法被即时处理也无法进入等待队列而被拒绝。
一开始我还在寻思着如何模拟上面说的参数配置的流程,后面一想,我们springboot一启动起来不就是一个天然的Tomcat线程池吗,
上面我们已经配置过了对应参数,编写一个controller用于请求
接下来我们只要模拟客户端去访问刚刚的controller请求即可,我们简单粗暴一点,直接新开一个线程代表一个客户端的请求。
我们用最简单的方式验证最多只有20个线程在同时工作,直接ctrl+f查找对应的关键字,发现只有20个!
这里总的线程是200个,而我们设置的最大max-connections是100,所以超过以后的都会拒绝!
Spring Boot 本身并不提供一个默认的线程池配置,而是依赖于 Spring Framework 中 抽象的默认配置。在 Spring Framework 中, 是用来处理多线程任务的抽象接口,而 是其一个具体的实现,它使用了一个 Java 的 作为其核心。
在springboot当中,根据 官方文档的说明,如果没有配置线程池的话,springboot会自动配置一个ThreadPoolTaskExecutor 线程池到bean当中,我们只需要按照他的方式调用就可以了!!
- ThreadPoolExecutor:这个是JAVA自己实现的线程池执行类,基本上创建线程池都是通过这个类进行的创建!
- ThreadPoolTaskExecutor :这个是springboot基于ThreadPoolExecutor实现的一个线程池执行类。
Spring Boot在没有自定义线程池配置的情况下,会自动配置一个作为默认线程池。根据官方文档和相关资源,以下是默认的线程池参数:
这些参数可以通过在或文件中设置来进行自定义调整。例如:
在 Spring Boot 中,默认的线程池由 类负责创建,它通常使用 来配置默认的线程池。虽然默认的线程池参数可以根据不同的 Spring Boot 版本或特定配置而有所不同,但通常情况下,Spring Boot 默认的线程池参数如下:
在包的是SpringBoot默认的任务执行自动配置类。
从可以知道开启了属性绑定到的实体类上
打个断点可以看到默认参数如下:
进入到类中,看到属性绑定以为前缀。默认线程池的核心线程数,最大线程数,以及任务等待队列
因为的值为2147483647(2的31次方-1),所以默认情况下,一般任务队列就可能把内存给堆满了。我们真正使用的时候,还需要对异步任务的执行线程池做一些基础配置,以防止出现内存溢出导致服务不可用的问题。
我们可以通过在应用的配置文件(如 或 )中自定义这些参数,以满足我们的一些特定需求。
但是如果需要更加精确地控制线程池的参数,您也可以在配置类中自定义一个 bean,并根据具体需求设置相应的参数。
以下是一个示例,展示如何在 Spring Boot 应用中配置一个自定义的线程池:
这里使用很简单,只需要像普通的spring bean一样注入即可,它会去根据name匹配 @Bean(“taskExecutor”) 这个线程池
如果是使用的@Async注解,只需要在注解里面指定bean的名称就可以切换到对应的线程池去了。如下所示:
请注意,Spring Boot 提供了自动配置选项,可以根据应用程序的类路径和其他条件自动配置 。例如,如果你使用 注解并且没有显式配置 ,Spring Boot 可能会为你提供一个默认的 实现。但是,这个默认配置的具体参数取决于 Spring Boot 的版本和你的应用程序的依赖。
这里的请求流程其实就是我们常见的JDK的线程池流程
假如我们的application.properties配置参数如下,接下来我们来看看,根据不同的线程数对应的请求流程是怎么样的:
下面是根据这些配置参数,不同的线程数时对应的请求流程:
请求流程概述:
需要注意的是,如果线程池的任务拒绝策略没有被显式配置,默认的拒绝策略是抛出一个 。如果需要别的的拒绝策略,可以在配置中自定义
不知道有没有细心的小伙伴注意到,在探讨Tomcat线程池与Spring Boot默认线程池的区别时,我们发现了一个显著的差异,就是它们处理请求的方式。让我们以相同的配置参数为例:核心线程数为10,最大线程数为20,任务队列容量为30。
Tomcat线程池的工作流程:
Spring Boot默认线程池(JDK线程池)的工作流程:
本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕,E-mail:xinmeigg88@163.com
本文链接:http://www.ksxb.net/tnews/2826.html