在高并发Java系统中,线程池是资源管控与性能优化的核心组件。错误的线程池配置,轻则导致系统吞吐量下降、响应延迟飙升,重则引发OOM、服务雪崩。本文从底层源码逻辑出发,系统拆解线程池的核心设计、执行流程、参数选型与生产级实战方案,帮你彻底打通线程池的任督二脉。
一、线程池的核心本质与顶层架构
1.1 池化思想的核心价值
线程池的本质是池化思想在并发编程中的落地,核心价值体现在四个维度:
- 降低资源损耗:避免频繁创建、销毁线程带来的CPU与内存开销
- 提升响应速度:任务到达时可直接复用已有线程,无需等待线程创建
- 管控资源上限:通过控制最大并发线程数,避免系统资源被无限耗尽
- 统一监控与调优:提供标准化的指标采集与生命周期管理能力
1.2 JDK线程池顶层架构
JDK线程池的核心实现基于一套分层的接口设计,通过模板方法模式解耦任务提交、执行与生命周期管理,架构关系如下:
各核心组件的职责:
- Executor:顶层抽象接口,仅定义
execute(Runnable)方法,彻底解耦任务提交与任务执行 - ExecutorService:扩展生命周期管理能力,新增任务提交(submit/invokeAll)、线程池关闭等核心方法
- AbstractExecutorService:模板方法抽象类,实现submit、invoke等通用任务提交逻辑,下沉公共实现
- ThreadPoolExecutor:线程池核心实现类,包含线程池的全量核心逻辑,是本文讲解的核心主体
- ScheduledThreadPoolExecutor:定时任务线程池实现,继承ThreadPoolExecutor,支持延迟、周期任务执行
二、线程池的生命周期
线程池有5个明确的生命周期状态,状态流转严格受控,是线程池安全运行的基础保障,状态流转流程如下:
各状态的核心定义:
- RUNNING:正常运行状态,可接受新任务,持续处理队列中的等待任务
- SHUTDOWN:温和关闭状态,拒绝新任务提交,继续处理队列中已有的任务
- STOP:强制关闭状态,拒绝新任务提交,中断正在执行的任务,清空队列中未执行的任务
- TIDYING:清理状态,所有任务已终止,工作线程数归0,即将执行
terminated()钩子方法 - TERMINATED:终止状态,
terminated()方法执行完成,线程池彻底销毁
❝核心区别:
shutdown()会等待所有任务执行完成,适用于正常关闭场景;shutdownNow()会强制中断正在执行的任务,返回未执行的任务列表,适用于紧急停机场景。
三、线程池核心参数全解与设计逻辑
ThreadPoolExecutor的核心构造函数包含7个核心参数,每个参数直接决定线程池的运行行为,是参数调优的核心基础。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
3.1 corePoolSize 核心线程数
核心线程数是线程池长期保留的存活线程数量,即使线程处于空闲状态,也不会被回收(除非开启allowCoreThreadTimeOut)。
- 核心设计:核心线程是线程池的常驻线程,用于应对日常流量,避免频繁创建线程
- 预热机制:可通过
prestartCoreThread()预热单个核心线程,prestartAllCoreThreads()预热所有核心线程,解决系统启动时首次请求的延迟问题 - 超时回收:开启
allowCoreThreadTimeOut(true)后,核心线程空闲超过keepAliveTime也会被回收,适用于突发流量后资源释放的场景
3.2 maximumPoolSize 最大线程数
最大线程数是线程池允许创建的最大工作线程数量,是系统资源管控的核心上限。
- 核心逻辑:只有当核心线程数已满、工作队列也已满时,线程池才会创建新的非核心线程,直到线程数达到maximumPoolSize
- 高频误区:90%的开发者会错误认为任务执行顺序是「核心线程→最大线程→队列」,正确顺序是「核心线程→队列→最大线程」
- 失效场景:当使用无界工作队列时,队列永远不会满,maximumPoolSize参数完全失效,永远不会创建非核心线程
3.3 keepAliveTime & unit 空闲线程存活时间
keepAliveTime是非核心线程空闲后的最大存活时间,unit是对应的时间单位。
- 核心作用:峰值流量过后,回收空闲的非核心线程,释放系统资源
- 生效范围:默认仅对非核心线程生效,开启
allowCoreThreadTimeOut(true)后,对核心线程同样生效 - 选型建议:短任务场景可设置较小的值,快速释放资源;长任务场景可设置较大的值,避免频繁创建线程
3.4 workQueue 工作队列
工作队列是用于存储等待执行任务的阻塞队列,是线程池调优、流量削峰的核心组件。常用阻塞队列的特性与适用场景如下:
| 队列类型 | 核心特性 | 适用场景 | 风险点 |
| ArrayBlockingQueue | 有界队列,基于数组实现,必须指定容量,FIFO顺序 | 稳定流量的业务场景,生产环境首选 | 容量设置过小会频繁触发拒绝策略 |
| LinkedBlockingQueue | 基于链表实现,默认容量为Integer.MAX_VALUE(无界),可指定容量 | 可控流量的任务处理,需严格指定容量 | 不指定容量时会无限积压任务,引发OOM,导致maximumPoolSize失效 |
| SynchronousQueue | 不存储元素的阻塞队列,插入操作必须等待对应移除操作 | 短任务、突发流量场景,任务直接递交给线程 | 需配合合理的maximumPoolSize,否则会无限创建线程 |
| DelayedWorkQueue | 延迟队列,基于堆实现,支持任务按延迟时间排序 | 定时任务、延迟重试场景 | 任务执行时间不可控会导致后续任务延迟 |
| PriorityBlockingQueue | 支持优先级的无界阻塞队列,任务按优先级排序执行 | 有优先级区分的任务场景 | 无界特性存在OOM风险,低优先级任务可能出现饥饿 |
3.5 threadFactory 线程工厂
线程工厂用于创建线程池的工作线程,是生产环境排查问题、异常管控的核心组件。
- 核心价值:为线程设置有意义的名称,问题排查时可快速定位到对应线程池;设置线程优先级、守护线程状态;为线程设置未捕获异常处理器,避免异常被静默吞掉
- 生产要求:必须自定义线程工厂,禁止使用默认的DefaultThreadFactory,默认工厂生成的线程名称无业务含义,无法定位问题
3.6 handler 拒绝策略
拒绝策略是线程池的流量保护机制,当核心线程、工作队列、最大线程数均已满,无法处理新任务时,会触发预设的拒绝策略,是系统高可用的最后一道防线。
四、线程池核心执行流程
线程池的任务执行逻辑完全由execute()方法定义,是线程池的核心灵魂,执行流程如下:
执行流程的核心细节:
- 任务提交后,首先判断当前运行的核心线程数是否小于corePoolSize,若是则创建新的核心线程执行任务
- 若核心线程数已满,尝试将任务加入工作队列,入队成功则等待空闲线程执行
- 若队列已满,判断当前线程数是否小于maximumPoolSize,若是则创建非核心线程执行任务
- 若线程数已达到maximumPoolSize,触发拒绝策略处理新任务
❝核心补充:
execute()与submit()的核心区别
execute()仅支持提交Runnable任务,无返回值,任务运行时异常会直接抛出,可被异常处理器捕获submit()支持提交Runnable/Callable任务,返回Future对象,任务异常会被封装到Future中,只有调用get()方法时才会抛出异常,不调用则异常会被完全静默吞掉
五、拒绝策略全解与生产级实战
5.1 JDK内置拒绝策略
JDK内置了4种拒绝策略,均实现了RejectedExecutionHandler接口,可直接使用:
- AbortPolicy:默认拒绝策略,直接抛出
RejectedExecutionException异常,中断任务提交。适用于绝大多数业务场景,可让调用方快速感知任务被拒绝,及时做降级处理 - CallerRunsPolicy:调用者运行策略,由提交任务的线程直接执行该任务。不会丢弃任务、不会抛出异常,会阻塞任务提交线程,降低任务提交速度,实现自动流量削峰。适用于日志写入、非核心统计等不允许任务丢失的场景
- DiscardPolicy:丢弃策略,直接静默丢弃新提交的任务,不抛出任何异常。仅适用于完全非核心、可丢失的任务,生产环境需谨慎使用
- DiscardOldestPolicy:丢弃最老任务策略,移除队列中等待最久的任务,重新提交当前任务。适用于消息通知、最新数据优先的场景,禁止与优先级队列配合使用
5.2 自定义拒绝策略实战
生产环境中,内置拒绝策略往往无法满足业务需求,需自定义拒绝策略实现日志记录、告警、降级、任务持久化等能力。
package com.jam.demo.threadpool;
import com.alibaba.fastjson2.JSON;
import com.jam.demo.entity.FailTask;
import com.jam.demo.mapper.FailTaskMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.Date;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 自定义业务拒绝策略
* @author ken
*/
@Slf4j
@Component
public class BusinessRejectedExecutionHandler implements RejectedExecutionHandler {
private final FailTaskMapper failTaskMapper;
public BusinessRejectedExecutionHandler(FailTaskMapper failTaskMapper) {
this.failTaskMapper = failTaskMapper;
}
/**
* 拒绝任务处理逻辑
* @param r 被拒绝的任务
* @param executor 当前线程池实例
*/
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("任务被线程池拒绝,线程池信息:核心线程数={},最大线程数={},活跃线程数={},队列剩余容量={}",
executor.getCorePoolSize(),
executor.getMaximumPoolSize(),
executor.getActiveCount(),
executor.getQueue().remainingCapacity());
try {
if (!ObjectUtils.isEmpty(r)) {
FailTask failTask = new FailTask();
failTask.setTaskContent(JSON.toJSONString(r));
failTask.setCreateTime(new Date());
failTask.setTaskStatus(0);
failTask.setRetryCount(0);
failTaskMapper.insert(failTask);
log.info("拒绝任务持久化成功,任务信息:{}", failTask);
}
} catch (Exception e) {
log.error("拒绝任务持久化失败", e);
}
throw new RuntimeException("线程池任务被拒绝,已触发降级处理",
new java.util.concurrent.RejectedExecutionException("Task " + r.toString() +
" rejected from " + executor.toString()));
}
}
六、高并发场景线程池选型与参数配置
线程池没有万能配置,必须结合业务场景的任务类型、流量特征进行设计,核心选型逻辑如下。
6.1 按任务类型选型配置
6.1.1 CPU密集型任务
任务特征:主要消耗CPU资源,如加密解密、数据压缩、数值计算、内存排序等,线程运行时CPU利用率高,等待时间极少。
- 核心配置公式:
核心线程数 = CPU核心数 + 1 - 设计逻辑:避免过多线程导致频繁的CPU上下文切换,最大化CPU利用率,+1是为了应对线程页缺失、意外暂停导致的CPU空闲
- 队列选型:ArrayBlockingQueue有界队列,容量不宜过大
- 拒绝策略:AbortPolicy,快速感知失败
6.1.2 IO密集型任务
任务特征:主要消耗IO资源,如数据库查询、RPC调用、文件读写、网络请求等,线程大部分时间处于等待状态,CPU利用率低。
- 基础配置公式:
核心线程数 = CPU核心数 * 2 - 精准配置公式:
核心线程数 = CPU核心数 * (1 + 平均等待时间 / 平均工作时间) - 设计逻辑:线程等待IO时CPU处于空闲状态,可通过更多线程充分利用CPU资源
- 队列选型:ArrayBlockingQueue有界队列,可设置稍大的容量应对流量波动
- 拒绝策略:CallerRunsPolicy或自定义降级策略,避免任务丢失
6.1.3 混合型任务
任务特征:同时包含CPU密集与IO密集操作,是绝大多数业务系统的常见场景。
- 核心方案:线程池隔离,将CPU密集型与IO密集型任务拆分到不同的线程池,避免慢IO任务占用所有线程,导致CPU密集任务无法执行
- 设计逻辑:不同类型任务的线程数配置、队列选型完全不同,隔离后可独立调优,避免互相影响
- 进阶方案:核心业务与非核心业务线程池隔离,避免非核心业务异常拖垮核心业务
6.1.4 定时任务场景
任务特征:延迟执行、周期执行的任务,如定时统计、状态同步、超时检查等。
- 实现选型:ScheduledThreadPoolExecutor,禁止使用Timer(单线程、异常会导致所有任务停止)
- 核心配置:核心线程数根据任务数量与执行周期设置,通常为
CPU核心数,最大线程数固定为Integer.MAX_VALUE(队列特性决定不会触发) - 关键要求:任务内部必须捕获所有异常,未捕获的异常会导致后续周期任务完全停止执行
- 队列选型:内置DelayedWorkQueue,无需自定义
6.2 生产环境选型禁忌
- 禁止使用Executors创建线程池:Executors的静态方法均存在OOM风险,newFixedThreadPool/newSingleThreadExecutor使用无界LinkedBlockingQueue,newCachedThreadPool最大线程数为Integer.MAX_VALUE,生产环境必须通过ThreadPoolExecutor手动创建
- 禁止使用无界队列:除非能严格控制任务提交数量,否则无界队列会导致任务无限积压,内存占用飙升,最终引发OOM
- 禁止所有任务共用一个线程池:不同类型、不同优先级的任务必须做线程池隔离,避免慢任务拖垮整个系统
- 禁止将maximumPoolSize设置为Integer.MAX_VALUE:会导致无限创建线程,耗尽系统内存与CPU资源
- 禁止忽略线程池关闭:方法内创建的局部线程池,必须在finally中关闭;容器管理的线程池,需通过@PreDestroy注解在容器销毁时关闭,避免线程泄露
七、代码实战
7.1 项目依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<groupId>com.jam</groupId>
<artifactId>threadpool-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>threadpool-demo</name>
<properties>
<java.version>17</java.version>
<guava.version>33.1.0-jre</guava.version>
<fastjson2.version>2.0.52</fastjson2.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<springdoc.version>2.5.0</springdoc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
7.2 自定义线程工厂实现
package com.jam.demo.threadpool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 自定义业务线程工厂
* @author ken
*/
@Slf4j
public class BusinessThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String threadNamePrefix;
private final boolean daemon;
private final int priority;
public BusinessThreadFactory(String threadNamePrefix) {
this(threadNamePrefix, false, Thread.NORM_PRIORITY);
}
public BusinessThreadFactory(String threadNamePrefix, boolean daemon, int priority) {
if (!StringUtils.hasText(threadNamePrefix)) {
throw new IllegalArgumentException("线程名称前缀不能为空");
}
this.threadNamePrefix = threadNamePrefix + "-thread-";
this.daemon = daemon;
this.priority = priority;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, threadNamePrefix + threadNumber.getAndIncrement());
thread.setDaemon(daemon);
thread.setPriority(priority);
thread.setUncaughtExceptionHandler((t, e) -> {
log.error("线程{}发生未捕获异常", t.getName(), e);
});
return thread;
}
}
7.3 线程池配置类
package com.jam.demo.config;
import com.jam.demo.threadpool.BusinessRejectedExecutionHandler;
import com.jam.demo.threadpool.BusinessThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池配置类
* @author ken
*/
@Configuration
public class ThreadPoolConfig {
private static final int CPU_CORE_SIZE = Runtime.getRuntime().availableProcessors();
private final BusinessRejectedExecutionHandler rejectedExecutionHandler;
public ThreadPoolConfig(BusinessRejectedExecutionHandler rejectedExecutionHandler) {
this.rejectedExecutionHandler = rejectedExecutionHandler;
}
/**
* CPU密集型任务线程池
*/
@Bean("cpuIntensiveThreadPool")
public ThreadPoolExecutor cpuIntensiveThreadPool() {
int corePoolSize = CPU_CORE_SIZE + 1;
int maximumPoolSize = corePoolSize;
return new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new BusinessThreadFactory("cpu-intensive"),
rejectedExecutionHandler
);
}
/**
* IO密集型任务线程池
*/
@Bean("ioIntensiveThreadPool")
public ThreadPoolExecutor ioIntensiveThreadPool() {
int corePoolSize = CPU_CORE_SIZE * 2;
int maximumPoolSize = corePoolSize * 2;
return new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(500),
new BusinessThreadFactory("io-intensive"),
rejectedExecutionHandler
);
}
/**
* 通用业务线程池
*/
@Bean("businessThreadPool")
public ThreadPoolExecutor businessThreadPool() {
return new ThreadPoolExecutor(
10,
20,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200),
new BusinessThreadFactory("common-business"),
rejectedExecutionHandler
);
}
/**
* 定时任务线程池
*/
@Bean("scheduledThreadPool")
public java.util.concurrent.ScheduledExecutorService scheduledThreadPool() {
return new java.util.concurrent.ScheduledThreadPoolExecutor(
CPU_CORE_SIZE,
new BusinessThreadFactory("scheduled-task"),
rejectedExecutionHandler
);
}
}
7.4 线程池监控实现
package com.jam.demo.monitor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池监控组件
* @author ken
*/
@Slf4j
@Component
public class ThreadPoolMonitor {
private final Map<String, ThreadPoolExecutor> threadPoolExecutorMap;
public ThreadPoolMonitor(Map<String, ThreadPoolExecutor> threadPoolExecutorMap) {
this.threadPoolExecutorMap = threadPoolExecutorMap;
}
/**
* 每分钟采集线程池指标
*/
@Scheduled(fixedRate = 60 * 1000)
public void collectThreadPoolMetrics() {
threadPoolExecutorMap.forEach((poolName, executor) -> {
log.info("线程池[{}]监控指标:核心线程数={},最大线程数={},当前线程数={},活跃线程数={},队列大小={},队列剩余容量={},已完成任务数={}",
poolName,
executor.getCorePoolSize(),
executor.getMaximumPoolSize(),
executor.getPoolSize(),
executor.getActiveCount(),
executor.getQueue().size(),
executor.getQueue().remainingCapacity(),
executor.getCompletedTaskCount());
if (executor.getQueue().remainingCapacity() < 50) {
log.warn("线程池[{}]队列剩余容量不足,即将触发拒绝策略", poolName);
}
if (executor.getActiveCount() >= executor.getMaximumPoolSize() * 0.9) {
log.warn("线程池[{}]活跃线程数已超过最大线程数90%,系统负载过高", poolName);
}
});
}
}
7.5 线程池使用与异常处理示例
package com.jam.demo.controller;
import com.google.common.collect.Lists;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池使用示例接口
* @author ken
*/
@Slf4j
@RestController
@RequestMapping("/threadpool")
@Tag(name = "线程池示例接口", description = "线程池使用与异常处理示例")
public class ThreadPoolDemoController {
private final ThreadPoolExecutor ioIntensiveThreadPool;
public ThreadPoolDemoController(ThreadPoolExecutor ioIntensiveThreadPool) {
this.ioIntensiveThreadPool = ioIntensiveThreadPool;
}
@GetMapping("/execute/demo")
@Operation(summary = "execute方法使用示例", description = "execute方法提交任务,异常直接抛出")
public String executeDemo() {
ioIntensiveThreadPool.execute(() -> {
log.info("execute方法执行任务开始");
int result = 1 / 0;
log.info("execute方法执行任务结束,结果:{}", result);
});
return "任务提交成功";
}
@GetMapping("/submit/demo")
@Operation(summary = "submit方法使用示例", description = "submit方法提交任务,异常正确处理方式")
public String submitDemo() {
List<Future<Integer>> futureList = Lists.newArrayList();
for (int i = 0; i < 5; i++) {
int finalI = i;
Future<Integer> future = ioIntensiveThreadPool.submit(() -> {
log.info("submit方法执行任务{}开始", finalI);
if (finalI == 3) {
throw new RuntimeException("任务执行异常");
}
return finalI * 2;
});
futureList.add(future);
}
if (!CollectionUtils.isEmpty(futureList)) {
futureList.forEach(future -> {
try {
Integer result = future.get();
log.info("任务执行结果:{}", result);
} catch (Exception e) {
log.error("任务执行异常", e);
}
});
}
return "任务提交成功";
}
@GetMapping("/dynamic/adjust")
@Operation(summary = "线程池参数动态调整", description = "生产环境动态调整线程池核心参数")
public String dynamicAdjust() {
ioIntensiveThreadPool.setCorePoolSize(20);
ioIntensiveThreadPool.setMaximumPoolSize(40);
ioIntensiveThreadPool.setKeepAliveTime(30, java.util.concurrent.TimeUnit.SECONDS);
log.info("线程池参数动态调整完成");
return "参数调整成功";
}
}
7.6 失败任务持久化实体与Mapper
package com.jam.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* 失败任务实体
* @author ken
*/
@Data
@TableName("t_fail_task")
@Schema(description = "失败任务实体")
public class FailTask {
@TableId(type = IdType.AUTO)
@Schema(description = "主键ID")
private Long id;
@Schema(description = "任务内容")
private String taskContent;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "任务状态 0-待重试 1-重试成功 2-重试失败")
private Integer taskStatus;
@Schema(description = "重试次数")
private Integer retryCount;
@Schema(description = "最后重试时间")
private Date lastRetryTime;
}
package com.jam.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.FailTask;
import org.apache.ibatis.annotations.Mapper;
/**
* 失败任务Mapper
* @author ken
*/
@Mapper
public interface FailTaskMapper extends BaseMapper<FailTask> {
}
对应的MySQL表结构:
CREATE TABLE `t_fail_task` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`task_content` text COMMENT '任务内容',
`create_time` datetime NOT NULL COMMENT '创建时间',
`task_status` tinyint NOT NULL DEFAULT '0' COMMENT '任务状态 0-待重试 1-重试成功 2-重试失败',
`retry_count` int NOT NULL DEFAULT '0' COMMENT '重试次数',
`last_retry_time` datetime DEFAULT NULL COMMENT '最后重试时间',
PRIMARY KEY (`id`),
KEY `idx_task_status` (`task_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='线程池失败任务表';
八、高频误区与避坑指南
- 执行顺序误区:错误认为任务执行顺序是「核心线程→最大线程→队列」,正确顺序是「核心线程→队列→最大线程」,队列满了才会触发非核心线程创建
- 线程数设置误区:认为核心线程数越大,系统吞吐量越高。实际线程数超过CPU核心数后,会引发频繁的上下文切换,导致吞吐量下降、延迟升高
- 无界队列误区:使用无界LinkedBlockingQueue不指定容量,导致任务无限积压引发OOM,同时maximumPoolSize参数完全失效
- 异常处理误区:使用submit()提交任务时,不调用Future.get()方法,导致任务异常被静默吞掉,无法感知业务错误
- 线程池共用误区:所有业务共用一个线程池,慢IO任务占用所有线程,导致核心业务任务无法执行,引发系统雪崩
- 容器环境误区:容器环境下直接使用
Runtime.getRuntime().availableProcessors()获取CPU核心数,会获取到宿主机的核心数,导致线程数设置过大,需使用容器感知的CPU核心数获取方式 - 定时任务误区:定时任务未捕获异常,导致单个任务异常后,后续所有周期任务完全停止执行
- 线程池关闭误区:方法内创建的局部线程池未关闭,导致线程泄露,最终耗尽系统资源
九、总结
线程池的本质是资源管控,而非单纯的提升性能。正确使用线程池,需要深入理解其底层执行逻辑,结合业务场景的任务类型、流量特征设计参数,做好线程池隔离、异常处理、监控告警与拒绝策略降级。没有万能的线程池配置,只有贴合业务的最优设计。只有吃透底层原理,才能在高并发场景中设计出稳定、高效的线程池方案,避免踩坑,保障系统的高可用。