Skip to content

线程池


一、线程池概述

是什么?
线程池是一种管理多线程的机制,通过预先创建一组线程并复用它们执行任务,避免频繁创建/销毁线程的开销。核心组件包括工作队列、线程集合、任务提交接口和拒绝策略。

解决什么问题

  1. 资源消耗:减少线程创建/销毁的系统开销(CPU、内存)
  2. 响应速度:任务到达时直接使用空闲线程执行,无需等待线程创建
  3. 可控性:限制并发线程数量,防止系统过载
  4. 线程管理:统一管理线程生命周期和异常处理

应用场景

  • Web服务器处理请求
  • 批量数据处理(如日志分析)
  • 异步任务执行(如发送邮件)
  • 高并发场景(如电商秒杀)

二、核心参数(ThreadPoolExecutor)

JDK中通过 ThreadPoolExecutor 类配置线程池,关键参数:

  1. corePoolSize(核心线程数):常驻线程数量,即使空闲也不销毁
  2. maximumPoolSize(最大线程数):允许创建的最大线程数
  3. keepAliveTime(空闲线程存活时间):非核心线程空闲时的存活时长
  4. unit(时间单位):如 TimeUnit.SECONDS
  5. workQueue(任务队列):存放待执行任务的阻塞队列
  6. threadFactory(线程工厂):自定义线程创建逻辑
  7. rejectedExecutionHandler(拒绝策略):任务过多时的处理规则

Java示例

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,      // corePoolSize
    10,     // maximumPoolSize
    60,     // keepAliveTime
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100),  // 任务队列容量100
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()  // 拒绝策略
);

三、工作原理

  1. 提交任务:调用 execute(Runnable task) 提交任务
  2. 流程判断
    • 若当前线程数 < corePoolSize → 创建新线程执行
    • 若线程数 ≥ corePoolSize → 任务入队
    • 若队列满且线程数 < maxPoolSize → 创建新线程
    • 若队列满且线程数 ≥ maxPoolSize → 触发拒绝策略
  3. 线程复用:线程执行完任务后从队列获取新任务
  4. 空闲回收:非核心线程空闲超过 keepAliveTime 后被回收

四、拒绝策略

当任务队列满且线程数达到 maxPoolSize 时触发:

策略类型实现类行为适用场景
AbortPolicyThreadPoolExecutor.AbortPolicy抛出 RejectedExecutionException需严格监控的任务
CallerRunsPolicyThreadPoolExecutor.CallerRunsPolicy由提交任务的线程直接执行避免任务丢失的慢速场景
DiscardPolicyThreadPoolExecutor.DiscardPolicy静默丢弃新任务可容忍丢失的任务(如日志)
DiscardOldestPolicyThreadPoolExecutor.DiscardOldestPolicy丢弃队列中最旧的任务,重试提交允许牺牲旧任务的场景

Java示例

java
// 自定义拒绝策略:记录日志后丢弃任务
RejectedExecutionHandler customHandler = (task, executor) -> {
    System.err.println("Task rejected: " + task);
};

五、线程池种类(Executors工厂类)

JDK提供快速创建线程池的方法(但生产环境建议手动配置 ThreadPoolExecutor):

  1. FixedThreadPool

    java
    ExecutorService fixedPool = Executors.newFixedThreadPool(5);
    • 固定线程数(corePoolSize = maxPoolSize)
    • 使用无界队列 LinkedBlockingQueue
    • 风险:队列堆积可能导致OOM
  2. CachedThreadPool

    java
    ExecutorService cachedPool = Executors.newCachedThreadPool();
    • 线程数无上限(maxPoolSize=Integer.MAX_VALUE)
    • 空闲线程60秒回收
    • 风险:线程过多可能导致CPU过载
  3. SingleThreadExecutor

    java
    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
    • 单线程串行执行
    • 适用于需顺序执行的任务
  4. ScheduledThreadPool (JDK8+)

    java
    ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
    scheduledPool.scheduleAtFixedRate(() -> System.out.println("Task"), 1, 5, TimeUnit.SECONDS);
    • 支持定时/周期性任务
    • 核心参数:corePoolSize + DelayedWorkQueue

六、重要注意事项

  1. 资源耗尽风险
    • FixedThreadPool/SingleThreadExecutor 使用无界队列 → 可能OOM
    • CachedThreadPool 线程数无上限 → 可能CPU/内存过载
  2. 线程泄漏:确保任务正确处理异常,避免线程因异常退出
  3. 监控工具
    • 使用 executor.getPoolSize() 监控线程数
    • 通过JMX或Spring Actuator监控线程池状态
  4. 合理配置
    • CPU密集型任务:线程数 ≈ CPU核心数
    • IO密集型任务:线程数 ≈ CPU核心数 * (1 + 等待时间/计算时间)
  5. 优雅关闭
    java
    executor.shutdown();          // 停止接收新任务
    executor.awaitTermination(10, TimeUnit.SECONDS);  // 等待已有任务完成

七、总结

维度要点
核心价值降低资源开销、提高响应速度、控制并发度
关键配置corePoolSizemaxPoolSizeworkQueuerejectedExecutionHandler
拒绝策略AbortPolicy(默认)、CallerRunsPolicy等4种
线程池类型Fixed/Cached/SingleThread/Scheduled
最佳实践生产环境手动配置 ThreadPoolExecutor,避免使用无界队列

💡 建议:JDK8+推荐使用 CompletableFuture 结合线程池实现异步编程,或使用Spring的 @Async 注解简化开发。