线程池深度解析:从使用到源码,再到生产实践
一、为什么需要线程池?
1.1 线程的代价
在开始讲解线程池之前,我们先看一个简单的例子:
java
// 原始方式:每次请求都创建新线程
public void handleRequest(Request request) {
new Thread(() -> {
// 处理业务逻辑
process(request);
}).start();
}
这种方式有什么问题?
- 创建成本高:每次创建线程需要分配内存、初始化栈(默认1MB)
- 资源浪费:线程创建/销毁消耗CPU资源
- 难以管理:线程数量无限制,容易导致系统崩溃
1.2 线程池的救赎
java
// 使用线程池后的优雅方式
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
public void handleRequest(Request request) {
threadPool.execute(() -> {
process(request);
});
}
二、Java线程池家族
2.1 Executors工厂类
Java提供了4种常用的线程池:
java
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 适用场景:已知并发量,需要控制资源
// 2. 单线程线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 适用场景:任务需要顺序执行
// 3. 可缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 适用场景:短期异步任务,线程数自动调整
// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
// 适用场景:延迟执行或周期性任务
2.2 这些"快捷方式"的真相
我们点开源码看看:
java
// Executors.newFixedThreadPool源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, // 核心线程数
nThreads, // 最大线程数
0L, TimeUnit.MILLISECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<Runnable>() // 无界队列
);
}
注意坑点 :LinkedBlockingQueue默认是Integer.MAX_VALUE,任务堆积可能OOM!
三、深入ThreadPoolExecutor源码
3.1 核心参数解析
java
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(长期存活的线程)
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
3.2 线程池工作流程(源码级解析)
java
// 简化版的execute方法流程
public void execute(Runnable command) {
// 第一阶段:核心线程处理
if (workerCount < corePoolSize) {
if (addWorker(command, true)) // 创建核心线程
return;
}
// 第二阶段:任务入队
if (isRunning() && workQueue.offer(command)) {
// 再次检查
if (!isRunning() && remove(command))
reject(command);
else if (workerCount == 0)
addWorker(null, false);
}
// 第三阶段:创建非核心线程
else if (!addWorker(command, false))
// 第四阶段:拒绝策略
reject(command);
}
让我们用流程图更直观地理解:
markdown
任务提交
↓
当前线程数 < corePoolSize?
├── 是 → 创建核心线程执行任务
↓
└── 否 → 工作队列未满?
├── 是 → 任务入队等待
↓
└── 否 → 当前线程数 < maximumPoolSize?
├── 是 → 创建临时线程执行
↓
└── 否 → 执行拒绝策略
3.3 Worker内部类:线程池的真正执行者
java
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 实际执行任务的线程
Runnable firstTask; // 初始任务
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this); // 核心执行方法
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
while (task != null || (task = getTask()) != null) {
// 获取锁,确保线程不会被其他任务中断
w.lock();
try {
// 执行前的钩子方法
beforeExecute(wt, task);
try {
task.run(); // 执行任务!
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++; // 统计完成任务数
w.unlock();
}
}
processWorkerExit(w, completedAbruptly);
}
}
3.4 getTask():线程如何获取任务
java
private Runnable getTask() {
boolean timedOut = false; // 上次poll是否超时
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查线程池状态
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 判断是否允许超时回收线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 关键:从队列取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
四、拒绝策略详解
4.1 Java内置的4种拒绝策略
java
// 1. AbortPolicy(默认) - 直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task rejected");
}
// 2. CallerRunsPolicy - 让调用者线程执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run(); // 注意:是run()不是start()!
}
}
// 3. DiscardPolicy - 默默丢弃
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 什么都不做,直接丢弃
}
// 4. DiscardOldestPolicy - 丢弃队列最老任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 丢弃队头
e.execute(r); // 尝试重新执行
}
}
4.2 自定义拒绝策略实战
java
@Component
public class CustomRejectPolicy implements RejectedExecutionHandler {
private final MeterRegistry meterRegistry;
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 1. 记录指标
meterRegistry.counter("threadpool.rejected.tasks").increment();
// 2. 降级策略
if (r instanceof ImportantTask) {
// 重要任务:记录日志,等待重试
log.warn("重要任务被拒绝,放入重试队列");
retryQueue.offer((ImportantTask) r);
} else {
// 普通任务:异步持久化,稍后处理
asyncSaveToDB(r);
}
// 3. 告警通知
if (needAlert(executor)) {
sendAlert(executor);
}
}
}
五、生产环境线程池配置方案
5.1 线程数计算公式
java
// 根据任务类型设置线程数
public class ThreadPoolConfig {
/**
* CPU密集型任务(计算、加密等)
* 线程数 = CPU核心数 + 1
*/
public static ExecutorService cpuIntensivePool() {
int cpuCores = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
cpuCores + 1,
cpuCores + 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1000)
);
}
/**
* IO密集型任务(网络请求、数据库操作等)
* 线程数 = CPU核心数 * (1 + 等待时间/计算时间)
* 通常设置为 CPU核心数 * 2 ~ 5
*/
public static ExecutorService ioIntensivePool() {
int cpuCores = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(
cpuCores * 2,
cpuCores * 5,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000)
);
}
/**
* 混合型任务
* 最佳线程数 = CPU核心数 * (1 + 阻塞系数)
* 阻塞系数 = 阻塞时间 / (阻塞时间 + 计算时间)
*/
public static ExecutorService mixedPool(double blockingCoefficient) {
int cpuCores = Runtime.getRuntime().availableProcessors();
int optimalThreads = (int) (cpuCores * (1 + blockingCoefficient));
return new ThreadPoolExecutor(
optimalThreads,
optimalThreads * 2,
30L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(optimalThreads * 10)
);
}
}
5.2 Spring Boot中的最佳配置
yaml
# application.yml
thread-pool:
configs:
order-process:
core-size: 8
max-size: 16
queue-capacity: 100
keep-alive-seconds: 60
thread-name-prefix: order-
reject-policy: CALLER_RUNS
payment-callback:
core-size: 4
max-size: 8
queue-capacity: 200
thread-name-prefix: payment-
reject-policy: ABORT
report-generate:
core-size: 2
max-size: 4
queue-capacity: 1000
thread-name-prefix: report-
allow-core-thread-timeout: true
java
@Configuration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {
@Bean
public Map<String, ExecutorService> threadPools(
ThreadPoolProperties properties) {
Map<String, ExecutorService> pools = new HashMap<>();
properties.getConfigs().forEach((name, config) -> {
// 动态参数配置
DynamicThreadPoolExecutor executor =
new DynamicThreadPoolExecutor(
config.getCoreSize(),
config.getMaxSize(),
config.getKeepAliveSeconds(),
TimeUnit.SECONDS,
createQueue(config.getQueueCapacity()),
new CustomThreadFactory(config.getThreadNamePrefix()),
createRejectPolicy(config.getRejectPolicy())
);
// 允许核心线程超时
if (config.isAllowCoreThreadTimeout()) {
executor.allowCoreThreadTimeOut(true);
}
// 注册监控
ThreadPoolMonitor.register(name, executor);
pools.put(name, executor);
});
return pools;
}
private BlockingQueue<Runnable> createQueue(int capacity) {
// 根据队列大小选择队列类型
if (capacity <= 0) {
return new SynchronousQueue<>();
} else if (capacity <= 10000) {
return new ArrayBlockingQueue<>(capacity);
} else {
return new LinkedBlockingQueue<>(capacity);
}
}
}
5.3 动态线程池:美团动态线程池实践
java
@Component
public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {
private final String poolName;
private final ThreadPoolProperties properties;
public DynamicThreadPoolExecutor(String poolName,
ThreadPoolProperties properties) {
super(properties.getCoreSize(),
properties.getMaxSize(),
properties.getKeepAliveSeconds(),
TimeUnit.SECONDS,
new ResizableCapacityLinkedBlockingQueue<>(
properties.getQueueCapacity()
));
this.poolName = poolName;
this.properties = properties;
// 初始化监听
initListeners();
}
/**
* 动态调整核心线程数
*/
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize >= 0 &&
corePoolSize <= getMaximumPoolSize()) {
super.setCorePoolSize(corePoolSize);
// 记录变更
log.info("线程池{}核心线程数调整为: {}",
poolName, corePoolSize);
}
}
/**
* 动态调整最大线程数
*/
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize >= getCorePoolSize()) {
super.setMaximumPoolSize(maximumPoolSize);
// 记录变更
log.info("线程池{}最大线程数调整为: {}",
poolName, maximumPoolSize);
}
}
/**
* 动态调整队列容量
*/
public void setQueueCapacity(int capacity) {
ResizableCapacityLinkedBlockingQueue<Runnable> queue =
(ResizableCapacityLinkedBlockingQueue<Runnable>) getQueue();
queue.setCapacity(capacity);
log.info("线程池{}队列容量调整为: {}", poolName, capacity);
}
}
// 可调整容量的队列
public class ResizableCapacityLinkedBlockingQueue<E>
extends LinkedBlockingQueue<E> {
private volatile int capacity;
public ResizableCapacityLinkedBlockingQueue(int capacity) {
super(capacity);
this.capacity = capacity;
}
public synchronized void setCapacity(int newCapacity) {
if (newCapacity <= 0) {
throw new IllegalArgumentException();
}
// 调整容量
this.capacity = newCapacity;
// 如果新容量小于当前size,需要移除多余元素
while (size() > newCapacity) {
poll();
}
}
@Override
public boolean offer(E e) {
// 动态判断是否接受新任务
if (size() >= capacity) {
return false;
}
return super.offer(e);
}
}
六、线程池监控方案
6.1 监控指标体系
java
@Component
public class ThreadPoolMonitor {
private final MeterRegistry meterRegistry;
public void monitor(String poolName, ThreadPoolExecutor executor) {
// 1. 核心指标
monitorCoreMetrics(poolName, executor);
// 2. 队列指标
monitorQueueMetrics(poolName, executor);
// 3. 任务执行指标
monitorTaskMetrics(poolName, executor);
// 4. 线程状态指标
monitorThreadMetrics(poolName, executor);
}
private void monitorCoreMetrics(String poolName,
ThreadPoolExecutor executor) {
// 线程数指标
Gauge.builder("threadpool.core.size",
executor, ThreadPoolExecutor::getCorePoolSize)
.tag("pool", poolName)
.register(meterRegistry);
Gauge.builder("threadpool.active.count",
executor, ThreadPoolExecutor::getActiveCount)
.tag("pool", poolName)
.register(meterRegistry);
Gauge.builder("threadpool.largest.pool.size",
executor, ThreadPoolExecutor::getLargestPoolSize)
.tag("pool", poolName)
.register(meterRegistry);
}
private void monitorQueueMetrics(String poolName,
ThreadPoolExecutor executor) {
BlockingQueue<Runnable> queue = executor.getQueue();
Gauge.builder("threadpool.queue.size",
queue, BlockingQueue::size)
.tag("pool", poolName)
.register(meterRegistry);
Gauge.builder("threadpool.queue.remaining",
queue, q -> q.remainingCapacity())
.tag("pool", poolName)
.register(meterRegistry);
}
}
6.2 可视化监控面板(Grafana)
json
{
"panels": [
{
"title": "线程池状态",
"targets": [
{
"expr": "threadpool_active_count{pool=\"$pool\"}",
"legendFormat": "活跃线程"
},
{
"expr": "threadpool_core_size{pool=\"$pool\"}",
"legendFormat": "核心线程"
},
{
"expr": "threadpool_max_size{pool=\"$pool\"}",
"legendFormat": "最大线程"
}
]
},
{
"title": "队列监控",
"targets": [
{
"expr": "threadpool_queue_size{pool=\"$pool\"}",
"legendFormat": "队列大小"
},
{
"expr": "threadpool_queue_remaining{pool=\"$pool\"}",
"legendFormat": "队列剩余容量"
}
]
}
]
}
6.3 告警规则配置
yaml
# alert-rules.yml
groups:
- name: threadpool_alerts
rules:
- alert: ThreadPoolQueueFull
expr: threadpool_queue_remaining{pool="order-process"} == 0
for: 1m
annotations:
summary: "订单线程池队列已满"
description: "{{ $labels.pool }}队列已满,持续1分钟"
- alert: ThreadPoolRejectedTasksHigh
expr: rate(threadpool_rejected_tasks_total{pool="payment-callback"}[5m]) > 10
for: 30s
annotations:
summary: "支付回调线程池拒绝任务过多"
description: "{{ $labels.pool }}5分钟内拒绝任务超过10个"
- alert: ThreadPoolActiveThreadsHigh
expr: threadpool_active_count{pool="report-generate"} / threadpool_max_size{pool="report-generate"} > 0.8
for: 2m
annotations:
summary: "报表生成线程池使用率过高"
description: "{{ $labels.pool }}活跃线程数达到最大线程数的80%"
七、常见问题与解决方案
7.1 线程池中的线程异常怎么办?
java
public class SafeThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// 处理未捕获异常
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
// 记录异常并采取恢复措施
log.error("线程池任务执行异常", t);
handleException(t);
}
}
private void handleException(Throwable t) {
// 1. 发送告警
alertSystem.send(t);
// 2. 重试机制
if (canRetry(t)) {
retryQueue.offer(currentTask);
}
// 3. 降级处理
fallbackService.process(currentTask);
}
}
7.2 如何优雅关闭线程池?
java
@Component
public class ThreadPoolShutdownHandler {
@PreDestroy
public void shutdownAll() {
// 1. 拒绝新任务
threadPool.shutdown();
try {
// 2. 等待现有任务完成(最多等待30分钟)
if (!threadPool.awaitTermination(30, TimeUnit.MINUTES)) {
// 3. 强制关闭
List<Runnable> unfinishedTasks =
threadPool.shutdownNow();
// 4. 处理未完成的任务
handleUnfinishedTasks(unfinishedTasks);
// 5. 再次等待
if (!threadPool.awaitTermination(10, TimeUnit.MINUTES)) {
log.error("线程池无法正常关闭");
}
}
} catch (InterruptedException e) {
// 6. 重新中断
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
private void handleUnfinishedTasks(List<Runnable> tasks) {
// 持久化未完成任务
tasks.forEach(task -> {
if (task instanceof PersistableTask) {
taskPersistenceService.save((PersistableTask) task);
}
});
}
}
八、最佳实践总结
8.1 线程池配置清单
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| corePoolSize | CPU核心数+1~核心数*2 | 根据任务类型调整 |
| maximumPoolSize | corePoolSize的2~4倍 | 为突发流量预留 |
| keepAliveTime | 30~60秒 | 避免频繁创建线程 |
| workQueue | ArrayBlockingQueue | 有界队列,防止OOM |
| threadFactory | 自定义命名 | 方便问题排查 |
| rejectedPolicy | CallerRunsPolicy | 或自定义降级策略 |
8.2 线程池使用"七不要"
- 不要使用Executors的快捷方法
- 不要使用无界队列(LinkedBlockingQueue默认无界)
- 不要让任务抛出未捕获异常
- 不要在任务中无限循环而不检查中断
- 不要忘记关闭线程池
- 不要所有业务共用一个线程池
- 不要忽视线程池监控
8.3 推荐的线程池工具类
java
public class ThreadPoolUtils {
private static final Map<String, ExecutorService> POOLS =
new ConcurrentHashMap<>();
/**
* 获取或创建线程池
*/
public static ExecutorService getOrCreate(String poolName,
Supplier<ExecutorService> creator) {
return POOLS.computeIfAbsent(poolName, k -> creator.get());
}
/**
* 安全执行任务
*/
public static <T> CompletableFuture<T> safeSubmit(
ExecutorService executor,
Callable<T> task) {
CompletableFuture<T> future = new CompletableFuture<>();
executor.submit(() -> {
try {
T result = task.call();
future.complete(result);
} catch (Throwable t) {
future.completeExceptionally(t);
log.error("任务执行失败", t);
}
});
return future;
}
/**
* 优雅关闭所有线程池
*/
@PreDestroy
public static void shutdownAll() {
POOLS.forEach((name, pool) -> {
try {
pool.shutdown();
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
});
}
}
九、进阶:虚拟线程(Java 21+)
Java 21引入了虚拟线程,这是对线程模型的革命性改进:
java
// 创建虚拟线程
Thread virtualThread = Thread.ofVirtual()
.name("virtual-thread-", 0)
.start(() -> {
// 任务逻辑
});
// 使用虚拟线程的ExecutorService
ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
// 性能对比:10万个任务
long start = System.currentTimeMillis();
// 传统线程池:可能创建数千个平台线程
try (ExecutorService executor = Executors.newFixedThreadPool(200)) {
for (int i = 0; i < 100_000; i++) {
executor.submit(() -> {
Thread.sleep(100);
return "done";
});
}
}
// 虚拟线程:轻松处理
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 100_000; i++) {
executor.submit(() -> {
Thread.sleep(100);
return "done";
});
}
}
虚拟线程的优势:
- 轻量级:内存开销约2KB(平台线程约1MB)
- 高并发:轻松支持数百万个虚拟线程
- 兼容性:完全兼容现有Thread API
总结
线程池是Java并发编程的核心组件,正确使用线程池需要:
- 理解原理:掌握ThreadPoolExecutor的工作机制
- 合理配置:根据业务场景选择合适的参数
- 监控预警:建立完善的监控体系
- 动态调整:根据负载情况动态调整参数
- 优雅处理:做好异常处理和优雅关闭
记住:没有万能的线程池配置,只有最适合你业务场景的配置。通过监控、分析和调整,不断优化你的线程池配置,才能构建出稳定高效的系统。
希望这篇文章能帮助你全面掌握线程池!如果有任何问题,欢迎随时讨论。