Skip to content
清晨的一缕阳光
返回

Java 线程池实战指南

Java 线程池实战指南

线程池通过复用线程、控制并发和统一管理,显著提升系统性能与资源利用率。

一、核心价值

价值说明
降低开销避免频繁创建/销毁线程(单次约 10ms)
流量控制防止突发任务压垮系统
统一管理支持线程状态跟踪、任务队列监控

二、核心参数

2.1 ThreadPoolExecutor 构造函数

public ThreadPoolExecutor(
    int corePoolSize,              // 核心线程数
    int maximumPoolSize,           // 最大线程数
    long keepAliveTime,            // 空闲存活时间
    TimeUnit unit,                 // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,   // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

2.2 参数详解

参数作用配置建议
corePoolSize核心线程数(常驻)CPU 密集型:CPU 核数 +1
IO 密集型:CPU 核数×2
maximumPoolSize最大线程数(应急)根据系统负载调整
keepAliveTime非核心线程存活时间30 秒~数分钟
workQueue任务队列有界队列防 OOM
threadFactory线程工厂自定义命名方便排查
handler拒绝策略CallerRunsPolicy

三、工作流程

flowchart TD
    A[提交任务] --> B{核心线程<br>满?}
    B -->|否 | C[创建核心线程]
    B -->|是 | D{队列<br>满?}
    D -->|否 | E[任务入队]
    D -->|是 | F{最大线程<br>满?}
    F -->|否 | G[创建非核心线程]
    F -->|是 | H[触发拒绝策略]
    

流程说明

  1. 核心线程处理:< corePoolSize 创建新线程
  2. 任务入队:核心线程满,放入队列
  3. 扩容:队列满且 < maximumPoolSize,创建非核心线程
  4. 拒绝:队列和线程池均满,触发拒绝策略

四、拒绝策略

4.1 内置策略

策略行为适用场景
AbortPolicy抛出异常需快速失败的关键业务
CallerRunsPolicy调用者线程执行允许降级的非核心任务
DiscardPolicy静默丢弃可丢失的日志、统计
DiscardOldestPolicy丢弃最旧任务实时性要求高的场景

4.2 自定义策略

public class CustomRejectPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 1. 记录日志
        log.warn("任务被拒绝:{}", r.toString());
        
        // 2. 持久化到数据库
        saveToDatabase(r);
        
        // 3. 发送告警
        sendAlert();
    }
}

五、线程池类型

5.1 Executors 工厂方法

// ❌ 不推荐使用(可能 OOM)
ExecutorService fixed = Executors.newFixedThreadPool(10);
// 问题:LinkedBlockingQueue 无界

ExecutorService cached = Executors.newCachedThreadPool();
// 问题:maximumPoolSize = Integer.MAX_VALUE

ExecutorService single = Executors.newSingleThreadExecutor();
// 问题:异常后无法恢复

5.2 推荐配置

// CPU 密集型任务
ThreadPoolExecutor cpuPool = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors() + 1,
    Runtime.getRuntime().availableProcessors() + 1,
    60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),
    new CustomThreadFactory("cpu-pool"),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

// IO 密集型任务
ThreadPoolExecutor ioPool = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors() * 2,
    Runtime.getRuntime().availableProcessors() * 2,
    60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(2000),
    new CustomThreadFactory("io-pool"),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

六、最佳实践

6.1 优雅关闭

public void shutdown() {
    executor.shutdown(); // 停止接收新任务
    try {
        // 等待已提交任务完成
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow(); // 强制中断
        }
    } catch (InterruptedException e) {
        executor.shutdownNow();
        Thread.currentThread().interrupt();
    }
}

6.2 监控指标

// 活跃线程数
int active = executor.getActiveCount();

// 队列积压
int queueSize = executor.getQueue().size();

// 已完成任务
long completed = executor.getCompletedTaskCount();

// 累计任务
long total = executor.getTaskCount();

6.3 线程池隔离

// 不同业务使用独立线程池
ThreadPoolExecutor orderPool = createPool("order");
ThreadPoolExecutor paymentPool = createPool("payment");
ThreadPoolExecutor notificationPool = createPool("notification");

// 避免相互影响

七、常见问题

7.1 线程泄漏

问题:任务超时未完成,线程无法回收

解决

// 设置任务超时
Future<?> future = executor.submit(task);
future.get(5, TimeUnit.SECONDS); // 超时抛出异常

// 移除耗时任务
executor.remove(task);

7.2 死锁

问题:嵌套提交任务到同一线程池

解决

// 使用不同线程池
executor1.submit(() -> {
    executor2.submit(() -> {
        // 避免死锁
    });
});

八、总结

线程池核心要点:

  1. 核心参数:corePoolSize、maximumPoolSize、workQueue
  2. 工作流程:核心线程 → 队列 → 非核心线程 → 拒绝
  3. 拒绝策略:优先使用 CallerRunsPolicy
  4. 最佳实践:避免 Executors 工厂,手动创建 ThreadPoolExecutor
  5. 监控告警:活跃线程数、队列积压、拒绝次数

生产环境务必手动创建线程池,精细化控制参数,避免 OOM 风险。


分享这篇文章到:

上一篇文章
Java IO 演进之路
下一篇文章
AQS 深度解析