Skip to content

Java 线程池的底层结构?

约 1044 字大约 3 分钟

多线程与并发字节

2025-03-20

⭐ 题目日期:

字节 - 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)提交任务时:

  1. 核心线程未满:创建新Worker线程执行任务。
  2. 核心线程已满:任务入队到workQueue
  3. 队列已满且线程未达最大值:创建新非核心线程执行任务。
  4. 队列和线程均满:触发拒绝策略(如抛出异常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. 线程池状态转换

  1. RUNNING → SHUTDOWN:调用shutdown(),停止接受新任务。
  2. RUNNING/SHUTDOWN → STOP:调用shutdownNow(),中断所有线程。
  3. STOP → TIDYING:所有线程终止且任务队列为空。
  4. TIDYING → TERMINATED:调用terminated()钩子方法后进入终止状态。

7. 常见线程池类型

线程池实现方式
FixedThreadPoolcorePoolSize = maximumPoolSize,使用无界队列(易OOM)。
CachedThreadPoolcorePoolSize = 0maximumPoolSize = Integer.MAX_VALUE,适合短时异步任务。
SingleThreadExecutor单线程执行,保证任务顺序。
ScheduledThreadPool使用DelayedWorkQueue,支持定时/周期性任务。

8. 线程池监控与调优

  • 监控指标
    • getPoolSize():当前线程数。
    • getActiveCount():活跃线程数。
    • getCompletedTaskCount():已完成任务数。
    • getQueue().size():队列中待处理任务数。
  • 调优建议
    • CPU密集型任务:线程数 ≈ CPU核心数。
    • I/O密集型任务:线程数 ≈ CPU核心数 × (1 + 平均等待时间/计算时间)。
    • 避免使用无界队列,防止内存溢出。

总结

Java线程池通过核心参数控制线程生命周期工作队列管理任务缓冲Worker封装线程执行逻辑,实现了高效的线程复用和任务调度。理解其底层结构有助于合理配置线程池参数,优化系统性能并避免资源耗尽问题。