文章目录
- 文章导图
- Spring封装的几种线程池
- SpringBoot默认线程池
- TaskExecutionAutoConfiguration(SpringBoot 2.1后)
- 主要作用
- 优势
- 使用场景
- 如果没有它
- 2.1版本以后
- 如何查看参数
- 方式一:通过@Async注解--采用ThreadPoolTaskExecutor
- determineAsyncExecutor-查找决定使用哪个线程池
- 实操
- @Async注意点
- 方式二:直接注入 ThreadPoolTaskExecutor
- 2.1版本以前
- 方式一:通过@Async注解--采用SimpleAsyncTaskExecutor
- 方式二:直接注入 ThreadPoolTaskExecutor
- 直接注入报错
- 解决:自定义一个线程池
- 默认的线程池好用吗?
- TaskExecutionAutoConfiguration?
- SimpleAsyncTaskExecutor
- 线程池核心线程数和最大线程数设置指南
- 线程池参数介绍
- 线程数设置考虑因素
- CPU密集型任务的线程数设置
- IO密集型任务的线程数设置
- 实际应用中的线程数计算
- 生产环境中的线程数设置
- 线程池参数设置建议
- 注意事项
线程池系列文章可参考下表,目前已更新3篇,还剩1篇TODO…
线程池系列: | 文章 |
---|---|
Java基础线程池 | TODO… |
CompletableFuture线程池 | 从用法到源码再到应用场景:全方位了解CompletableFuture及其线程池 |
SpringBoot默认线程池(@Async和ThreadPoolTaskExecutor) | 探秘SpringBoot默认线程池:了解其运行原理与工作方式(@Async和ThreadPoolTaskExecutor) |
SpringBoot默认线程池和内置Tomcat线程池 | 你是否傻傻分不清SpringBoot默认线程池和内置Tomcat线程池? |
文章导图
Spring封装的几种线程池
Spring框架为了简化并发任务的处理,提供了几种线程池的封装实现,这些线程池可以直接在Spring应用程序中使用。以下是Spring中常见的几种线程池:
线程池类型 | 描述 | 特点 |
---|---|---|
SimpleAsyncTaskExecutor | 非阻塞的线程池,为每个任务创建新线程,不限制线程数量。 | 不重用线程,适合任务量不大的情况。 |
SyncTaskExecutor | 同步执行器,任务在调用线程中直接执行,不创建新线程。 | 无异步处理能力,适用于简单同步操作。 |
ConcurrentTaskExecutor | 包装类,允许任何实现了java.util.concurrent.Executor 的实现作为Spring的TaskExecutor 。 | 提供了对现有Executor 实现的适配。 |
ThreadPoolTaskExecutor | 功能丰富的线程池实现,允许配置核心线程数、最大线程数、队列容量等。 | 适合需要精细控制线程池参数的场合。 |
WorkManagerTaskExecutor | 适用于Java EE环境下的CommonJ规范 | 在WebLogic和WebSphere等应用服务器上有CommonJ 的 WorkManager 支持时使用 |
TaskExecutorAdapter | 适配器,允许将实现了java.util.concurrent.ExecutorService 的对象转换为Spring的TaskExecutor 。 | 为现有的ExecutorService 提供Spring的TaskExecutor 接口适配。 |
接下来我们重点看ThreadPoolTaskExecutor
和SimpleAsyncTaskExecutor(@Async)
SpringBoot默认线程池
SpringBoot默认的线程池就是ThreadPoolTaskExecutor,但是在SpringBoot不同版本是有区别的,下面先从这个关键的TaskExecutionAutoConfiguration说起
TaskExecutionAutoConfiguration(SpringBoot 2.1后)
/**
* EnableAutoConfiguration Auto-configuration for TaskExecutor.
*
* @author Stephane Nicoll
* @author Camille Vienot
* @since 2.1.0
*/
// 仅在类 ThreadPoolTaskExecutor 存在于 classpath 时才应用
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
// 确保前缀为 spring.task.execution 的属性配置项被加载到 bean TaskExecutionProperties 中
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
/**
* Bean name of the application TaskExecutor.
*/
public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
private final TaskExecutionProperties properties;
private final ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers;
private final ObjectProvider<TaskDecorator> taskDecorator;
public TaskExecutionAutoConfiguration(TaskExecutionProperties properties,
ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
ObjectProvider<TaskDecorator> taskDecorator) {
this.properties = properties;
this.taskExecutorCustomizers = taskExecutorCustomizers;
this.taskDecorator = taskDecorator;
}
// 定义 bean TaskExecutorBuilder taskExecutorBuilder
// 这是一个 TaskExecutor 构建器
@Bean
// 仅在该 bean 尚未被定义时才定义
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder() {
TaskExecutionProperties.Pool pool = this.properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder.queueCapacity(pool.getQueueCapacity());
builder = builder.corePoolSize(pool.getCoreSize());
builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive());
builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
builder = builder.customizers(this.taskExecutorCustomizers);
builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
return builder;
}
// 懒惰模式定义 bean ThreadPoolTaskExecutor applicationTaskExecutor,
// 基于容器中存在的 TaskExecutorBuilder
@Lazy
// 使用bean名称 : taskExecutor, applicationTaskExecutor
@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
// 仅在容器中不存在类型为 Executor 的 bean 时才定义
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
}
注意上面的源码注释,我们可以看到@since 2.1.0
,所以TaskExecutionAutoConfiguration
是SpringBoot2.1以后才有的,那么这个类有啥作用呢?
TaskExecutionAutoConfiguration
是 Spring Boot 提供的一个自动配置类,它的目的是简化 Spring 应用中异步任务执行和任务调度的配置。
主要作用
- 自动配置异步任务执行器(Executor):
TaskExecutionAutoConfiguration
会为应用配置一个默认的ThreadPoolTaskExecutor
,这是一个基于线程池的TaskExecutor
实现。它适用于应用中通过@Async
注解标注的异步方法的执行。如果你不提供自定义配置,Spring Boot 将会使用这个自动配置的执行器来执行异步任务。 - 自动配置任务调度器(Scheduler): 同样地,它也为应用配置一个默认的
ThreadPoolTaskScheduler
,这是用于任务调度的组件,支持@Scheduled
注解标注的方法。这个调度器允许你在应用中简便地安排定期执行的任务。 - 可通过应用配置文件来定制配置:
TaskExecutionAutoConfiguration
支持通过application.properties
或application.yml
配置文件来自定义任务执行和调度的相关参数,例如线程池大小、队列容量等。这些参数分别位于spring.task.execution
和spring.task.scheduling
命名空间下。
优势
- 简化配置:无需手动定义
Executor
或Scheduler
的Bean,大大减少了样板代码。 - 易于定制:通过配置文件即可调整线程池参数,而无需修改代码。
- 与 Spring 生态系统的集成:自动配置的执行器和调度器与 Spring 的
@Async
和@Scheduled
注解无缝集成。
使用场景
当你的应用需要执行异步任务或者按计划执行某些作业时,你通常会需要配置一个任务执行器或者任务调度器。在 Spring Boot 应用中,TaskExecutionAutoConfiguration
让这一步骤变得极为简单和自动化。
如果没有它
如果没有 TaskExecutionAutoConfiguration
,你需要手动配置线程池、异步执行器(TaskExecutor
)和任务调度器(TaskScheduler
)。即,你需要在你的 Spring 配置中显式地定义相关的Bean,并可能还要为这些Bean提供自定义的配置。
总结起来,TaskExecutionAutoConfiguration
通过自动配置和简化配置工作,大大提高了开发效率,允许开发者更加专注于业务逻辑编写,而无需担心底层线程池和执行器的配置。
2.1版本以后
2.1版本后有TaskExecutionAutoConfiguration自动配置类,会帮我们自动配置默认线程池
ThreadPoolTaskExecutor
线程池默认参数如下:
-
核心线程数 (corePoolSize): 8
-
最大线程数 (maxPoolSize): Integer.MAX_VALUE (无限制)
-
队列容量 (queueCapacity): Integer.MAX_VALUE (无限制)
-
空闲线程保留时间 (keepAliveSeconds): 60秒
-
线程池拒绝策略 (RejectedExecutionHandler): AbortPolicy(默认策略,超出线程池容量和队列容量时抛出RejectedExecutionException异常)
这些参数可以通过在application.properties
或application.yml
文件中设置来进行自定义调整。例如:
# 核心线程数,默认为8
spring.task.execution.pool.core-size
# 最大线程数,默认为Integer.MAX_VALUE
spring.task.execution.pool.max-size
# 任务等待队列容量,默认为Integer.MAX_VALUE
spring.task.execution.pool.queue-capacity
# 空闲线程等待时间,默认为60s。如果超过这个时间没有任务调度,则线程会被回收
spring.task.execution.pool.keep-alive
# 是否允许回收空闲的线程,默认为true
spring.task.execution.pool.allow-core-thread-timeout
# 线程名前缀
spring.task.execution.thread-name-prefix=task-
如何查看参数
在 Spring Boot 中,默认的线程池由 TaskExecutorBuilder
类负责创建,它通常使用 ThreadPoolTaskExecutor
来配置默认的线程池。虽然默认的线程池参数可以根据不同的 Spring Boot 版本或特定配置而有所不同,但通常情况下,Spring Boot 默认的线程池参数如下:
在org.springframework.boot.autoconfigure.task
包的TaskExecutionAutoConfiguration.java
是SpringBoot默认的任务执行自动配置类。
从@EnableConfigurationProperties(TaskExecutionProperties.class)
可以知道开启了属性绑定到TaskExecutionProperties.java
的实体类上
打个断点可以看到默认参数如下:
进入到TaskExecutionProperties.java
类中,看到属性绑定以spring.task.execution
为前缀。默认线程池的核心线程数coreSize=8
,最大线程数maxSize = Integer.MAX_VALUE
,以及任务等待队列queueCapacity = Integer.MAX_VALUE
因为Integer.MAX_VALUE
的值为2147483647(2的31次方-1),所以默认情况下,一般任务队列就可能把内存给堆满了。我们真正使用的时候,还需要对异步任务的执行线程池做一些基础配置,以防止出现内存溢出导致服务不可用的问题。
方式一:通过@Async注解–采用ThreadPoolTaskExecutor
determineAsyncExecutor-查找决定使用哪个线程池
我们的@Async会默认使用什么线程池呢?这里就涉及源码了,核心源码如下:org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
这段方法的核心作用是,根据@Async
注解和方法上是否有指定的限定符来决定一个方法应该使用哪个AsyncTaskExecutor
来执行异步任务。
-
方法首先尝试从一个缓存(
this.executors
)中查找并返回已经与方法关联的执行器。 -
如果缓存里没有,那么这个方法会尝试根据方法上的
@Async
注解中指定的限定符,从Spring应用程序上下文中查找对应的执行器。(比如@Async("taskExecutor")
就会去找程序中已有的定义好的name="taskExecutor"的Bean) -
如果没有指定限定符或找不到对应的Bean,它将尝试使用或创建一个默认的执行器。
- 如果是SpringBoot2.1以后,就会创建TaskExecutionAutoConfiguration帮我们自动配置好的默认线程池
ThreadPoolTaskExecutor
- 如果是SpringBoot2.1之前,TaskExecutionAutoConfiguration没有帮我们配置默认线程池,最终会找到
org.springframework.core.task.SimpleAsyncTaskExecutor
- 如果是SpringBoot2.1以后,就会创建TaskExecutionAutoConfiguration帮我们自动配置好的默认线程池
-
创建或确定执行器后,方法会将其存进缓存中,以备下次执行同一方法时使用。
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
// 尝试从缓存中获取已确定的执行器
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor; // 声明一个目标执行器
String qualifier = getExecutorQualifier(method); // 获取方法上的@Async注解中指定的执行器的限定名
// 如果注解中有限定名则尝试根据该名字从BeanFactory中查找对应的执行器
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {
// 如果未指定限定名,则使用默认的执行器
targetExecutor = this.defaultExecutor;
// 如果默认执行器为空,则尝试从BeanFactory中获取默认的执行器
if (targetExecutor == null) {
synchronized (this.executors) {
// 双重检查锁定,确保只有一个线程可以初始化defaultExecutor
if (this.defaultExecutor == null) {
this.defaultExecutor = getDefaultExecutor(this.beanFactory);
}
targetExecutor = this.defaultExecutor;
}
}
}
// 如果没有合适的目标执行器,返回null
if (targetExecutor == null) {
return null;
}
// 如果目标执行器是AsyncListenableTaskExecutor则直接使用,否则使用适配器封装以确保它实现了AsyncListenableTaskExecutor
//所以SpringBoot2.1之前最终会找到org.springframework.core.task.SimpleAsyncTaskExecutor
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
// 将确定的执行器放入缓存,以便下次直接使用
this.executors.put(method, executor);
}
// 返回确定的AsyncTaskExecutor
return executor;
}
这个方法首先查找类型为 TaskExecutor
的Bean,如果有多个此类型的Bean,然后尝试查找名称为 “taskExecutor” 的Bean。如果这两种方式都没找到,那么将返回 null
,表示没有找到适当的Bean来执行异步任务。这种情况下,AsyncExecutionInterceptor
将不会有执行器来处理异步方法。
// 会去 Spring 的容器中找有没有 TaskExecutor 或名称为 'taskExecutor' 为 Executor 的
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
try {
return beanFactory.getBean(TaskExecutor.class);
} catch (NoUniqueBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
} catch (NoSuchBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
}
}
return null;
}
实操
@Service
public class SyncService {
@Async
public void testAsync1() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
@Async
public void testAsync2() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
}
注意需要在启动类上需要添加@EnableAsync
注解,否则不会生效。
@RestController
public class TestController {
@Autowired
SyncService syncService;
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
syncService.testAsync1();
syncService.testAsync2();
}
}
请求后观察控制台输出结果,打印线程名如下,说明我们这里采用的是TaskExecutionAutoConfiguration帮我们配置的默认线程池ThreadPoolTaskExecutor
,就是以task-
开头的
task-2
task-1
@Async注意点
-
启动类上需要添加
@EnableAsync
注解 -
注解的方法必须是public方法。
-
注解的方法不要定义为static
-
方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
Spring 基于AOP 实现了异步,因此,需要获取到容器中的代理对象才能具有异步功能。假定原对象叫obj
,容器中的代理对象叫做 objproxy
,在执行 fun()
会同时执行aop带来的其他方法。但是如果在fun()
中调用了 fun2()
,那 fun2()
是原始的方法,而不是经过aop代理后的方法,就不会具有异步的功能。
方式二:直接注入 ThreadPoolTaskExecutor
2.1版本以后我们有TaskExecutionAutoConfiguration自动配置类,它会去配置默认的线程池,此时注入的ThreadPoolTaskExecutor就是默认的线程池
@RestController
public class TestController {
@Resource
ThreadPoolTaskExecutor taskExecutor; //SpringBoot2.1之前直接注入会报错
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
ThreadPoolExecutor threadPoolExecutor = taskExecutor.getThreadPoolExecutor();
}
}
通过debug可以看到默认的线程池参数如下,就是我们上面介绍的
2.1版本以前
方式一:通过@Async注解–采用SimpleAsyncTaskExecutor
最主要的就是此时我们没有TaskExecutionAutoConfiguration自动配置类,也就不会去配置默认的线程池,根据上面分析的
determineAsyncExecutor
方法,此时@Async会去采用默认的SimpleAsyncTaskExecutor
@Service
public class SyncService {
@Async
public void testAsync1() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
@Async
public void testAsync2() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
}
注意需要再启动类上需要添加@EnableAsync
注解,否则不会生效。
@RestController
public class TestController {
@Autowired
SyncService syncService;
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
syncService.testAsync1();
syncService.testAsync2();
}
}
验证输出结果,确实是采用了SimpleAsyncTaskExecutor
线程池:
SimpleAsyncTaskExecutor-1
SimpleAsyncTaskExecutor-2
方式二:直接注入 ThreadPoolTaskExecutor
最主要的就是此时我们没有TaskExecutionAutoConfiguration自动配置类,也就不会去配置默认的线程池,此时直接注入就会报错,所以需要自己定义!
直接注入报错
@RestController
public class TestController {
@Resource
ThreadPoolTaskExecutor taskExecutor; //SpringBoot2.1之前直接注入会报错
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
ThreadPoolExecutor threadPoolExecutor = taskExecutor.getThreadPoolExecutor();
}
}
此时发现在工程启动的时候就报错!
解决:自定义一个线程池
@Configuration
public class ThreadPoolConfiguration {
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数信息
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(50);
taskExecutor.setQueueCapacity(200);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("myExecutor--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
//修改拒绝策略为使用当前线程执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池
taskExecutor.initialize();
return taskExecutor;
}
}
此时运行debug,可以看到不会启动报错,对应的线程池参数也是我们自定义的了
注意当我们上面自定义了线程池疑惑,@Sync对应的线程池也是我们自定义的这个,不会再采用默认的SimpleAsyncTaskExecutor
默认的线程池好用吗?
TaskExecutionAutoConfiguration?
默认参数如下面所示:
-
核心线程数 (corePoolSize): 8
-
最大线程数 (maxPoolSize): Integer.MAX_VALUE (无限制)
-
队列容量 (queueCapacity): Integer.MAX_VALUE (无限制)
-
空闲线程保留时间 (keepAliveSeconds): 60秒
-
线程池拒绝策略 (RejectedExecutionHandler): AbortPolicy(默认策略,超出线程池容量和队列容量时抛出RejectedExecutionException异常)
那么可能会有什么问题?
- 资源耗尽风险:由于最大线程数设置为
Integer.MAX_VALUE
,在高并发场景下,如果任务持续涌入,理论上可以创建无数线程,这将迅速消耗系统资源(如内存、CPU),可能导致服务器崩溃或极端性能下降。实际上,即使操作系统允许创建如此多的线程,这种设置也是极度不可取的。 - 响应性降低:虽然核心线程数设置为8是比较合理的起始点,但无限制的最大线程数意味着在高负载下,线程创建不会受限,这可能会影响到系统的响应时间和整体稳定性。过多的线程上下文切换会消耗大量CPU资源,降低处理效率。
- 队列无限增长:队列容量也设置为
Integer.MAX_VALUE
,这意味着当线程池中的线程都在忙碌且有新任务到来时,这些任务将不断堆积在队列中。如果生产速率持续高于消费速率,队列将无限增长,最终可能导致OutOfMemoryError。 - 异常处理策略过于简单粗暴:采用
AbortPolicy
作为拒绝策略,在线程池和队列都满载的情况下,新的任务提交将直接抛出RejectedExecutionException
异常,而没有进行任何备份处理或降级策略,这可能会导致部分业务操作失败,且未被捕获处理的异常可能会影响到整个应用的稳定性。 - 缺乏灵活性和控制:对于不同的业务需求,线程池参数应当根据具体情况进行调整。例如,对响应时间敏感的服务可能需要更小的队列和更快的拒绝策略以避免任务长时间等待;而对于可延迟处理的任务,则可能需要更大的队列或不同的拒绝策略。
SimpleAsyncTaskExecutor
SimpleAsyncTaskExecutor
是Spring框架提供的一个简单的异步任务执行器,它并不是一个真正的线程池实现。它的主要特点是每次调用 #execute(Runnable)
方法时都会创建一个新的线程来执行任务。这意味着它不会重用线程,而是一次性的。
使用 SimpleAsyncTaskExecutor
可能会导致以下问题:
- 线程资源浪费: 每次执行任务时都创建新线程,这会导致线程的生命周期短暂,频繁地创建和销毁线程会消耗系统资源,包括CPU时间和内存。
- 系统资源耗尽: 在高并发场景下,如果大量任务同时提交,
SimpleAsyncTaskExecutor
可能会创建大量线程,这可能会迅速耗尽系统资源,如线程数限制和内存资源。 - 缺乏线程管理: 真正的线程池通常包含线程管理机制,如线程重用、线程池大小控制、任务队列管理等。
SimpleAsyncTaskExecutor
缺乏这些特性,因此在面对大量并发任务时可能无法有效地管理资源。 - 没有任务队列: 由于
SimpleAsyncTaskExecutor
不使用任务队列,它无法有效地缓冲任务。当任务提交速度超过执行速度时,新提交的任务可能会因为没有可用的线程而立即失败。 - 不适合长期运行的服务: 对于需要长期运行的服务,使用
SimpleAsyncTaskExecutor
可能会导致系统不稳定,因为它没有为长期运行的任务提供稳定的线程资源。
总结: SimpleAsyncTaskExecutor
可以简单地用于异步任务执行,但由于它的设计限制,一般不推荐在生产环境中使用,特别是在要求高并发或资源使用高效的场景下。在这些情况下,更推荐使用可配置、可复用线程的线程池,如ThreadPoolTaskExecutor
,因为它提供了更多的灵活性、效率和控制能力。
线程池核心线程数和最大线程数设置指南
线程池参数介绍
- 核心线程数:线程池中始终活跃的线程数量。
- 最大线程数:线程池能够容纳同时执行的最大线程数量。
线程数设置考虑因素
- CPU密集型任务:依赖CPU计算的任务,如循环计算等。
- IO密集型任务:任务执行过程中涉及等待外部操作,如数据库读写、网络请求、磁盘读写等。
CPU密集型任务的线程数设置
- 推荐设置:核心数 + 1。
- 原因:避免线程切换开销,同时允许一定程度的线程中断恢复。
IO密集型任务的线程数设置
- 推荐设置:2 * CPU核心数。
- 原因:IO操作期间,CPU可执行其他线程任务,提高资源利用率。
实际应用中的线程数计算
- 使用工具(如Java的Visual VM)来监测线程的等待时间和运行时间。
- 计算公式:(线程等待时间 / 线程总运行时间)+ 1 * CPU核心数。
生产环境中的线程数设置
- 理论值与实际值可能存在差异,需要通过压力测试来确定最优线程数。
- 压力测试:调整线程数,观察系统性能,找到最优解。
线程池参数设置建议
- 核心业务应用:核心线程数设置为压力测试后的数值,最大线程数可以适当增加。
- 非核心业务应用:核心线程数设置较低,最大线程数设置为压力测试结果
注意事项
- 线程数设置需根据实际业务需求和系统环境进行调整。
- 持续监控和优化是保证系统性能的关键。