线程池原理与类型详解
一、线程池核心原理
- 为什么需要线程池
• 降低资源消耗:减少线程创建和销毁的开销
• 提高响应速度:任务到达时,线程已存在,直接执行
• 统一管理:控制并发数量,避免系统崩溃
• 提供功能:支持定时、延迟、周期执行
- 核心组件
bash
// ThreadPoolExecutor 核心参数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
- 工作原理流程
新任务提交
↓
- 核心线程是否已满? ──否──→ 创建核心线程执行
↓是 - 工作队列是否已满? ──否──→ 任务入队等待
↓是 - 线程数是否达到最大? ──否──→ 创建非核心线程执行
↓是 - 执行拒绝策略
二、Java 内置线程池类型
- FixedThreadPool(固定大小)
bash
ExecutorService executor = Executors.newFixedThreadPool(5);
特点:
• 固定数量的核心线程
• 无界队列(LinkedBlockingQueue)
• 线程永不回收
• 适用:负载较重的服务器,需要限制线程数
- CachedThreadPool(缓存线程池)
bash
ExecutorService executor = Executors.newCachedThreadPool();
特点:
• 核心线程数 = 0,最大线程数 = Integer.MAX_VALUE
• 使用 SynchronousQueue(不存储元素)
• 空闲线程60秒后回收
• 适用:执行大量短期异步任务
- SingleThreadExecutor(单线程)
bash
ExecutorService executor = Executors.newSingleThreadExecutor();
特点:
• 只有一个线程
• 无界队列
• 保证任务顺序执行
• 适用:需要顺序执行的任务
- ScheduledThreadPool(定时线程池)
bash
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
特点:
• 支持定时、周期性执行
• 核心线程可回收
• 适用:定时任务、延时任务
- WorkStealingPool(工作窃取,Java 8+)
bash
ExecutorService executor = Executors.newWorkStealingPool();
特点:
• 基于 ForkJoinPool
• 使用工作窃取算法
• 并行处理任务
• 适用:计算密集型任务
三、线程池状态
bash
// ThreadPoolExecutor 中的状态定义
private static final int RUNNING = -1 << COUNT_BITS; // 运行中
private static final int SHUTDOWN = 0 << COUNT_BITS; // 关闭中(不接受新任务)
private static final int STOP = 1 << COUNT_BITS; // 停止(不处理队列任务)
private static final int TIDYING = 2 << COUNT_BITS; // 整理中
private static final int TERMINATED = 3 << COUNT_BITS; // 终止
四、拒绝策略
- AbortPolicy(默认)
bash
// 抛出 RejectedExecutionException
new ThreadPoolExecutor.AbortPolicy()
- CallerRunsPolicy
bash
// 由调用线程执行任务
new ThreadPoolExecutor.CallerRunsPolicy()
- DiscardPolicy
bash
// 直接丢弃任务,不抛异常
new ThreadPoolExecutor.DiscardPolicy()
- DiscardOldestPolicy
bash
// 丢弃队列中最旧的任务,然后重试
new ThreadPoolExecutor.DiscardOldestPolicy()
五、自定义线程池示例
bash
// 生产环境推荐自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲时间
new ArrayBlockingQueue<>(100), // 有界队列(防止内存溢出)
new ThreadFactory() { // 自定义线程工厂
private final AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "my-thread-" + count.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
六、线程池监控
bash
ThreadPoolExecutor executor = ...;
// 监控指标
executor.getPoolSize(); // 当前线程数
executor.getActiveCount(); // 活动线程数
executor.getQueue().size(); // 队列大小
executor.getCompletedTaskCount();// 完成任务数
executor.getTaskCount(); // 总任务数
七、最佳实践
- 设置合理的拒绝策略
// 根据业务需求选择
bash
new ThreadPoolExecutor.CallerRunsPolicy() // 重要任务,不能丢弃
new ThreadPoolExecutor.AbortPolicy() // 快速失败,便于排查
- 正确关闭线程池
bash
executor.shutdown(); // 温和关闭
executor.shutdownNow(); // 立即关闭
executor.awaitTermination(10, TimeUnit.SECONDS); // 等待关闭

- 线程泄漏问题
bash
// 使用 submit() - 异常被吞没
executor.submit(() -> {
try {
// 任务逻辑
} catch (Exception e) {
// 必须捕获异常,否则线程会退出,控制台没有任何异常输出!程序继续运行,就像什么都没发生
log.error("任务执行异常", e);
}
});
// 使用 execute() - 异常能抛出
executor.execute(() -> {
int result = 1 / 0; // ❌ 异常能抛出到控制台
});
总结
线程池是 Java 并发编程的核心组件,合理使用线程池可以:
- 提高系统性能
- 控制资源消耗
- 提供统一管理
- 增强系统稳定性
根据业务场景选择合适的线程池类型和参数配置是关键。