Java 线程池总结

一、写在前面

参考阿里开发规约,创建线程池一般用ThreadPoolExecutor

在高并发程序中,频繁创建与销毁线程是一种极其低效且不可控的行为。为了解决这个问题,Java 提供了线程池(ThreadPoolExecutor)这一强大的并发框架。它不仅提高了任务处理的效率,也帮助我们更好地控制系统资源,是现代高性能服务架构中的基石。

本文将从线程池的概念、设计目的、核心实现机制、任务调度流程、Worker 管理机制等方面入手


1.1 线程池是什么

线程池是一种线程复用机制,它维护了一组可以重复使用的工作线程,任务到来时无需创建新线程,而是复用已有线程执行任务,任务执行完毕后线程不会被销毁,而是回到池中待命。

Java 中的核心实现是 java.util.concurrent.ThreadPoolExecutor


1.2 线程池解决了什么问题

问题线程池的解决方案
线程创建销毁开销大线程复用,避免频繁构造/销毁
系统资源不可控线程数量上限可设,避免 OOM
并发请求过多无法及时处理使用阻塞队列缓存任务,异步处理
调度和控制困难提供任务调度、拒绝策略、监控接口
缺乏任务管理能力可统一提交、执行、取消任务


二、线程池核心设计与实现

2.1 总体设计架构

Java 线程池主要由以下组件构成:

┌──────────────┐     submit(Runnable)
│  主线程调用  ├────────────────┐
└──────────────┘                ▼
                        ┌──────────────┐
                        │任务提交 execute│
                        └─────┬────────┘
                              ▼
              ┌────────────┐    (1)创建核心线程执行任务
              │ WorkerSet  │ ←────────────────────────┐
              └────┬───────┘                          │
                   ▼                                  │
          ┌────────────────┐                          │
          │ BlockingQueue  │ ←───────(2)入队等待执行 │
          └──────┬─────────┘                          │
                 ▼                                    │
          (3)队列满,扩容线程池                  (4)满了,执行拒绝策略
                 ▼                                    ▼
        ┌────────────────┐                    ┌────────────────┐
        │ maximumPoolSize│                    │ rejectHandler()│
        └────────────────┘                    └────────────────┘


2.2 生命周期管理

线程池有五种状态,由高位表示,低位为线程数量(workerCount),统一由 ctl 原子变量管理。

状态描述
RUNNING接收新任务,处理队列
SHUTDOWN拒收新任务,继续处理队列
STOP拒收新任务,中断执行线程
TIDYING所有任务执行完,清理中
TERMINATED清理完成,彻底关闭

生命周期状态在任务提交、线程退出、关闭线程池等场景中都会动态变化。


2.3 任务执行机制

2.3.1 任务调度流程(execute)

executor.execute(task) 时的完整流程:

  1. 线程池运行状态检查,若不是 RUNNING,直接拒绝任务。

  2. 线程数 < corePoolSize:创建新线程执行任务。

  3. 线程数 ≥ corePoolSize:尝试将任务放入队列。

  4. 队列已满 && 线程数 < maximumPoolSize:创建非核心线程执行任务。

  5. 队列满 && 已达最大线程数:执行拒绝策略(默认抛异常)。


2.3.2 任务缓冲机制

任务队列用于缓存尚未执行的任务:

队列类型特性
ArrayBlockingQueue有界 FIFO 队列,避免内存溢出
LinkedBlockingQueue默认无界队列,适合任务速率平稳
SynchronousQueue不存储任务,适用于高并发
PriorityBlockingQueue支持优先级任务

任务是否入队,决定了线程是否扩容。


2.3.3 任务申请(线程取任务)

任务的获取由 getTask() 方法完成:

  • 优先从队列中 poll 一个任务

  • 若获取失败,且超出 keepAliveTime,线程会退出

  • 如果线程池关闭,线程立即退出


2.3.4 任务拒绝策略

当线程池无法接收任务时,会调用 RejectedExecutionHandler

策略描述
AbortPolicy(默认)抛出异常
DiscardPolicy直接丢弃任务
DiscardOldestPolicy丢弃队列中最老的任务
CallerRunsPolicy调用线程自己执行任务(削峰)

合理的拒绝策略有助于保护系统稳定性。


2.4 Worker 线程管理机制

2.4.1 Worker 线程

Worker 是线程池中用于执行任务的线程封装类,它本质是一个实现了 Runnable 接口的任务执行单元,每个 Worker 包含:

  • 一个真正的 Thread 对象

  • 一个任务任务引用

  • 一个 run() 循环体:从队列中不断拉取任务执行


2.4.2 Worker 线程的创建(addWorker)

线程池根据当前任务和线程数决定是否调用 addWorker() 增加线程:

  • 当线程数 < corePoolSize:创建核心线程

  • 当队列已满 && 线程数 < maximumPoolSize:创建临时线程

使用 ThreadFactory 可定制线程名称、优先级等。


2.4.3 Worker 线程的回收

非核心线程超过 keepAliveTime 会被自动回收:

  • 降低资源占用

  • 避免长期占用 CPU/内存

  • 通过 allowCoreThreadTimeOut(true) 也可使核心线程超时回收

回收逻辑在 getTask() 中实现。


2.4.4 Worker 线程执行任务流程

Worker 启动后,在 run() 方法中循环调用:

while ((task != null || (task = getTask()) != null)) {
    beforeExecute()
    try {
        task.run();
    } finally {
        afterExecute()
    }
}

执行完任务后尝试从队列中取下一个任务,直到线程超时或线程池关闭。


总结

Java 的线程池设计是非常经典的“并发容器”之一,其优雅地处理了:

  • 线程的 复用与销毁

  • 任务的 排队与拒绝

  • 系统的 稳定性与扩展性

它不是简单的任务线程分发器,更是一个具备“智能调度、弹性扩容、精细管控”能力的多线程平台。


附录:推荐配置建议

场景核心参数建议
接口服务corePoolSize = CPU 核心数,队列有限,拒绝策略为 CallerRuns
IO 密集maximumPoolSize 设置略高,使用 SynchronousQueue
定时任务使用 ScheduledThreadPoolExecutor
高并发网关使用自定义线程工厂 + 有界队列 + 指标监控
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值