外观
Java 线程池的底层结构?
⭐ 题目日期:
字节 - 2024/12/25
📝 题解:
Java线程池的底层结构主要由以下几个核心组件组成,这些组件协同工作以实现高效的线程管理和任务调度:
1. 核心参数
线程池通过以下参数控制其行为:
corePoolSize
:核心线程数,即使空闲也不会被回收,除非设置allowCoreThreadTimeOut
。maximumPoolSize
:最大线程数,当任务队列满且线程数未达此值时,创建新线程。keepAliveTime
:非核心线程的空闲存活时间,超时后回收。workQueue
:任务队列(如LinkedBlockingQueue
),存储待执行的任务。threadFactory
:线程工厂(如DefaultThreadFactory
),用于创建新线程。handler
:拒绝策略(如AbortPolicy
),当任务队列和线程池均满时处理新任务。
2. 核心数据结构
(1) 控制状态变量 ctl
- 类型:
AtomicInteger
,高3位表示线程池状态,低29位表示有效线程数。 - 状态:
- RUNNING:接受新任务并处理队列中的任务。
- SHUTDOWN:不接受新任务,但处理队列中的任务。
- STOP:不接受新任务,不处理队列任务,并中断正在执行的任务。
- TIDYING:所有任务已终止,线程池即将终止。
- TERMINATED:线程池完全终止。
(2) 工作线程 Worker
- 实现:继承自
AbstractQueuedSynchronizer
(AQS),简化锁管理。 - 作用:封装线程和任务执行逻辑,每个
Worker
对应一个线程。 - 生命周期:
- 创建时绑定初始任务。
- 通过
runWorker()
循环从队列中获取任务执行。 - 空闲超时或线程池关闭时终止。
3. 任务处理流程
当调用execute(Runnable command)
提交任务时:
- 核心线程未满:创建新
Worker
线程执行任务。 - 核心线程已满:任务入队到
workQueue
。 - 队列已满且线程未达最大值:创建新非核心线程执行任务。
- 队列和线程均满:触发拒绝策略(如抛出异常
RejectedExecutionException
)。
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
// 1. 当前线程数 < corePoolSize
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get();
}
// 2. 任务入队
if (isRunning(c) && workQueue.offer(command)) {
// 双重检查线程池状态
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 创建非核心线程(若队列已满)
else if (!addWorker(command, false))
// 4. 触发拒绝策略
reject(command);
}
4. 工作队列(BlockingQueue)
- 常用实现:
LinkedBlockingQueue
:无界或有界队列(默认无界,需谨慎避免OOM)。ArrayBlockingQueue
:有界队列,固定容量。SynchronousQueue
:不存储元素,直接传递任务(如CachedThreadPool
)。DelayedWorkQueue
:延迟队列(用于ScheduledThreadPool
)。
5. 拒绝策略(RejectedExecutionHandler)
AbortPolicy
(默认):抛出RejectedExecutionException
。CallerRunsPolicy
:由提交任务的线程直接执行任务。DiscardPolicy
:静默丢弃任务。DiscardOldestPolicy
:丢弃队列中最旧的任务,并重新提交当前任务。
6. 线程池状态转换
- RUNNING → SHUTDOWN:调用
shutdown()
,停止接受新任务。 - RUNNING/SHUTDOWN → STOP:调用
shutdownNow()
,中断所有线程。 - STOP → TIDYING:所有线程终止且任务队列为空。
- TIDYING → TERMINATED:调用
terminated()
钩子方法后进入终止状态。
7. 常见线程池类型
线程池 | 实现方式 |
---|---|
FixedThreadPool | corePoolSize = maximumPoolSize ,使用无界队列(易OOM)。 |
CachedThreadPool | corePoolSize = 0 ,maximumPoolSize = Integer.MAX_VALUE ,适合短时异步任务。 |
SingleThreadExecutor | 单线程执行,保证任务顺序。 |
ScheduledThreadPool | 使用DelayedWorkQueue ,支持定时/周期性任务。 |
8. 线程池监控与调优
- 监控指标:
getPoolSize()
:当前线程数。getActiveCount()
:活跃线程数。getCompletedTaskCount()
:已完成任务数。getQueue().size()
:队列中待处理任务数。
- 调优建议:
- CPU密集型任务:线程数 ≈ CPU核心数。
- I/O密集型任务:线程数 ≈ CPU核心数 × (1 + 平均等待时间/计算时间)。
- 避免使用无界队列,防止内存溢出。
总结
Java线程池通过核心参数控制线程生命周期、工作队列管理任务缓冲、Worker封装线程执行逻辑,实现了高效的线程复用和任务调度。理解其底层结构有助于合理配置线程池参数,优化系统性能并避免资源耗尽问题。