Java****线程池详解
1.1 什么是线程池
1.2 为什么需要线程池
1.3 线程池的核心参数
1.4 线程池工作原理
2.1 常见的线程池类型
2.1.1 FixedThreadPool
2.1.2 CachedThreadPool
2.1.3 SingleThreadExecutor
2.1.4 ScheduledThreadPool
2.2 线程池的使用示例
2.2.1 FixedThreadPool的使用
2.2.2 CachedThreadPool的使用
2.2.3 SingleThreadExecutor的使用
2.2.4 ScheduledThreadPool的使用
2.3 线程池的优缺点
2.3.1 线程池的优点
2.3.2 线程池的缺点
3.1 ThreadPoolExecutor的生命周期
3.2 ThreadPoolExecutor执行流程
3.3 线程池参数调优
3.3.1 核心线程数和最大线程数
3.3.2 工作队列的选择
3.3.3 拒绝策略的选择
4.1 常用阻塞队列类型
4.1.1 ArrayBlockingQueue
4.1.2 LinkedBlockingQueue
4.1.3 SynchronousQueue
4.1.4 PriorityBlockingQueue
4.1.5 DelayQueue
4.2 阻塞队列对线程池行为的影响
4.2.1 有界队列
4.2.2 无界队列
4.2.3 同步队列
4.3 队列选择指南
4.4 阻塞队列性能对比
5.1 内置监控功能
5.2 JMX监控
5.3 自定义线程池监控器
5.4 动态调整线程池配置
5.5 线程池监控最佳实践
6.1 线程池死锁与死锁检测
解决方案:
6.2 任务拒绝处理
自定义拒绝策略
6.3 线程泄漏问题场景一:忘记关闭线程池
场景二:线程被阻塞且无法中断
6.4 线程池性能问题与调优
常见性能问题
性能调优策略
6.5 在容器环境中使用线程池的注意事项
问题
解决方案
7.1 线程池创建与配置最佳实践
避免使用Executors工厂方法
线程池命名
线程池分离原则
7.2 任务提交最佳实践
使用合适的任务提交方法
避免任务依赖
设置任务超时
推荐使用CompletableFuture
7.3 异常处理最佳实践
处理未捕获的异常
异常统计与监控
7.4 关闭线程池最佳实践
优雅地关闭线程池
线程池关闭时的资源清理
7.5 生产环境中的线程池监控
关键监控指标
监控实现
7.6 线程池适用场景与不适用场景
适用场景
不适用场景
8.1 Spring框架中的线程池
TaskExecutor接口
Spring中的线程池配置
Spring的异步执行
Spring的定时任务调度
8.2 Tomcat中的线程池
Tomcat的线程模型
Tomcat线程池配置
Spring Boot中配置Tomcat线程池
Tomcat线程池的监控
8.3 Netty中的线程池
Netty的线程模型
自定义Netty线程池配置
使用业务线程池
8.4 MyBatis中的线程池
MyBatis的Executor
MyBatis-Spring与数据库连接池
8.5 RocketMQ中的线程池
RocketMQ的线程池架构
RocketMQ消费者线程池配置
自定义RocketMQ生产者的线程池
9.1 ForkJoinPool与工作窃取算法
9.1.1 ForkJoinPool原理
9.1.2 ForkJoinPool vs ThreadPoolExecutor9.1.3 工作窃取算法
9.1.4 RecursiveTask和RecursiveAction
9.2 CompletableFuture与异步编程
9.2.1 CompletableFuture基础
9.2.2 组合多个CompletableFuture
9.2.3 CompletableFuture与线程池配合使用
9.3 Java 19虚拟线程与结构化并发
9.3.1 虚拟线程基础
9.3.2 虚拟线程与传统线程池的比较
9.3.3 结构化并发
9.3.4 虚拟线程最佳实践
9.4 高并发系统中的线程池优化
9.4.1 线程池隔离策略
9.4.2 动态线程池
9.4.3 断路器模式与线程池
9.4.4 队列背压与限流
10.1 核心知识回顾
10.2 线程池发展趋势
10.3 后续学习路径
线程池是一种线程使用模式。线程池维护多个线程,等待着监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程的开销,另一方面避免了线程数量膨胀导致的过分调度问题。
线程池可以看作是线程的集合,通过使用线程池,我们可以:
在并发编程中,多线程技术已经成为提高系统性能的重要手段。但直接创建线程存在以下问题:
下面是一个对比示例,展示直接创建线程和使用线程池的区别:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
// 执行任务的数量
private static final int TASK_COUNT = 10000;
// 使用直接创建线程的方式执行任务
public static void executeWithDirectThreads() {
long start = System.currentTimeMillis();
for (int i = 0; i < TASK_COUNT; i++) {
Thread thread = new Thread(() -> {
// 模拟任务执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
}
long end = System.currentTimeMillis();
System.out.println("直接创建线程耗时: " + (end - start) + "ms");
}
// 使用线程池执行任务
public static void executeWithThreadPool() {
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < TASK_COUNT; i++) {
executor.execute(() -> {
// 模拟任务执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
try {
// 等待所有任务完成
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long end = System.currentTimeMillis();
System.out.println("使用线程池耗时: " + (end - start) + "ms");
}
public static void main(String[] args) {
executeWithDirectThreads();
executeWithThreadPool();
}
}
运行上面的代码,可以明显看到使用线程池执行任务比直接创建线程要快得多,并且资源利用更加合理。
Java中的线程池主要由ThreadPoolExecutor
类实现,它提供了一系列参数来配置线程池的行为:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
各参数详细介绍:
allowCoreThreadTimeOut
keepAliveTime
参数的时间单位,可以是毫秒、秒、分钟等线程池的工作原理如下图所示:
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ │ │ │
│ 任务提交 │ │ 任务执行 │
│ │ │ │
│ │ │ │
└─────────┬───────────┘ └─────────▲───────────┘
│ │
│ │
│ │
▼ │
┌─────────────────────┐ ┌────────┴────────┐
│ │ yes │ │
│ 核心线程池是否已满? ├──────►│ 创建新的线程 │
│ │ │ │
└─────────┬───────────┘ └─────────────────┘
│ no
│
▼
┌─────────────────────┐ ┌─────────────────────┐
│ │ yes │ │
│ 工作队列是否已满? ├──────►│最大线程池是否已满? │
│ │ │ │
└─────────┬───────────┘ └─────────┬───────────┘
│ no │
│ │ no
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ 将任务加入工作队列 │ │ 创建新的线程 │
│ │ │ │
└─────────────────────┘ └─────────────────────┘
│
│ yes
▼
┌─────────────────────┐
│ │
│ 执行拒绝策略 │
│ │
└─────────────────────┘
线程池的处理流程:
下面通过一个简单的示例来演示线程池的处理流程:
import java.util.concurrent.*;
public class ThreadPoolWorkingDemo {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, TimeUnit.SECONDS, // 线程存活时间
new ArrayBlockingQueue<>(2), // 工作队列容量为2
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 使用调用者运行策略
);
// 模拟10个任务提交到线程池
for (int i = 1; i <= 10; i++) {
final int taskId = i;
System.out.println("提交任务: " + taskId);
executor.execute(() -> {
System.out.println("开始执行任务: " + taskId +
", 线程名: " + Thread.currentThread().getName());
try {
// 模拟任务执行耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("完成任务: " + taskId +
", 线程名: " + Thread.currentThread().getName());
});
System.out.println("线程池状态: " +
"核心线程数: " + executor.getCorePoolSize() +
", 活动线程数: " + executor.getActiveCount() +
", 最大线程数: " + executor.getMaximumPoolSize() +
", 线程池大小: " + executor.getPoolSize() +
", 队列任务数: " + executor.getQueue().size() +
", 已完成任务数: " + executor.getCompletedTaskCount());
}
// 关闭线程池
executor.shutdown();
}
}
Java通过Executors
工厂类提供了几种常见的线程池实现,每种线程池都有其特定的使用场景:
FixedThreadPool
是一个固定大小的线程池,它的特点是:
LinkedBlockingQueue
// 创建固定大小为5的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
内部实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, // 核心线程数
nThreads, // 最大线程数(与核心线程数相同)
0L, // 线程存活时间(不会回收)
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界队列
);
}
CachedThreadPool
是一个会根据需要创建新线程的线程池,它的特点是:
SynchronousQueue
作为工作队列// 创建缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
内部实现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, // 核心线程数为0
Integer.MAX_VALUE, // 最大线程数非常大
60L, // 空闲线程存活时间
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>() // 同步队列
);
}
SingleThreadExecutor
是一个只有一个工作线程的线程池,它的特点是:
LinkedBlockingQueue
// 创建单线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
内部实现:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
1, // 核心线程数为1
1, // 最大线程数也为1
0L, // 线程存活时间(不会回收)
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界队列
));
}
ScheduledThreadPool
是一个可以定时执行任务的线程池,它的特点是:
DelayedWorkQueue
作为工作队列// 创建具有3个核心线程的ScheduledThreadPool
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
内部实现:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor的构造函数
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(
corePoolSize, // 指定核心线程数
Integer.MAX_VALUE, // 最大线程数非常大
0, // 线程存活时间
TimeUnit.NANOSECONDS,
new DelayedWorkQueue() // 延迟工作队列
);
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class FixedThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交10个任务给线程池执行
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.