一、核心关系

ExecutorsThreadPoolExecutor 的关系可以概括为:工厂与产品的关系。

Executors 是一个工具类(工厂类),提供了一系列静态工厂方法,用于快速创建不同类型的线程池实例。而 ThreadPoolExecutor 是具体的实现类,承载了线程池的核心逻辑与运行机制。

继承与实现链条:

ThreadPoolExecutor 实现了 ExecutorService 接口,而 ExecutorService 又继承了 Executor 接口。Executors 的工厂方法内部实际上就是调用了 ThreadPoolExecutor 的构造器,但使用了预设的参数配置,隐藏了部分细节。

关键认知: Executors 创建的线程池本质上都是 ThreadPoolExecutor 的实例(ScheduledThreadPoolExecutor 除外,它继承自 ThreadPoolExecutor 并实现了定时调度功能)。

二、本质区别

维度

Executors

ThreadPoolExecutor

角色定位

工厂类(Factory)

实现类(Implementation)

设计模式

静态工厂模式

构造者模式 + 直接构造

使用方式

快捷方法创建

精细参数配置

灵活性

预设配置,快速上手

完全可控,高度定制

风险性

隐藏细节,可能踩坑

暴露细节,需要理解

适用场景

简单业务、快速原型

生产环境、高并发系统

三、Executors 的快捷工厂方法

1. newFixedThreadPool(int nThreads)

// 源码实现:核心线程数等于最大线程数,空闲线程存活时间为 0(永不回收),使用无界链表队列。
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
        nThreads,                      // 核心线程数
        nThreads,                      // 最大线程数(相同)
        0L, TimeUnit.MILLISECONDS,     // 空闲线程存活时间(0表示永不回收)
        new LinkedBlockingQueue<>()     // 无界队列 ⚠️ 风险点
    );
}

隐患:任务无限堆积可能导致内存溢出(OOM)。当任务提交速度超过处理速度时,队列会无限增长。

2. newCachedThreadPool()

// 源码实现:核心线程数为 0,最大线程数为 Integer.MAX_VALUE(理论无限),空闲线程 60 秒后回收,使用同步移交队列。
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(
        0,                             // 核心线程数(0)
        Integer.MAX_VALUE,             // 最大线程数(无限)⚠️ 风险点
        60L, TimeUnit.SECONDS,
        new SynchronousQueue<>()        // 直接移交队列
    );
}

隐患:线程数可能无限增长,导致系统资源耗尽。适合短期异步任务,不适合长连接或持续高负载场景。

3. newSingleThreadExecutor()

// 单线程(核心和最大均为 1)配合无界队列,且通过包装类禁止外部直接访问 ThreadPoolExecutor 的方法(如 setCorePoolSize)。
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService(
        new ThreadPoolExecutor(
            1, 1, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>()  // 无界队列 ⚠️ 风险点
        )
    );
}

隐患: 同样存在无界队列导致的 OOM 风险,且无法动态调整参数。

4. newScheduledThreadPool(int corePoolSize)

// 定时任务专用,返回 ScheduledThreadPoolExecutor 实例,继承自 ThreadPoolExecutor,增加了定时调度能力。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

隐患: 虽然队列是 DelayedWorkQueue(有界),但最大线程数同样为 Integer.MAX_VALUE。

四、ThreadPoolExecutor 核心构造

完整参数构造器(7个参数)

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

任务提交流程

当提交一个新任务时,线程池按以下逻辑处理:

第一步: 判断当前运行线程数是否小于核心线程数。如果是,创建新线程执行任务(即使有空闲线程也会优先创建新线程直到达到核心数)。

第二步: 如果运行线程数已达核心数,判断任务队列是否已满。如果未满,将任务加入队列等待执行。

第三步: 如果队列已满,判断当前运行线程数是否小于最大线程数。如果是,创建非核心线程执行任务。

第四步: 如果运行线程数已达最大数,执行拒绝策略。

关键理解: 线程池优先扩队列,再扩线程。只有当队列满且线程数未达上限时,才会创建额外线程。

五、关键队列类型对比

队列类型

特性

适用场景

ArrayBlockingQueue

基于数组的有界阻塞队列,FIFO

明确容量限制,防止 OOM,需要预估队列大小

LinkedBlockingQueue

基于链表的可选有界/无界阻塞队列,默认无界

默认无界需注意内存,指定容量时与 ArrayBlockingQueue 类似但吞吐量更高

SynchronousQueue

零容量,不存储元素,直接移交

高吞吐场景,配合较大的最大线程数,任务直接交给线程执行

PriorityBlockingQueue

支持优先级排序的无界阻塞队列

任务有优先级差异,需要实现 Comparable 接口

DelayedWorkQueue

延迟排序队列,ScheduledThreadPoolExecutor 专用

定时任务调度,按延迟时间排序

六、拒绝策略详解

// 1. AbortPolicy(默认)- 直接抛出异常
// 直接抛出 RejectedExecutionException 异常,阻止系统继续接受新任务。
// 这是最直观的失败通知方式,但可能导致调用方异常
new ThreadPoolExecutor.AbortPolicy();

// 2. CallerRunsPolicy - 由调用线程执行
// 由提交任务的线程(调用者)自己执行该任务。
// 这相当于一种降级保护机制,让提交者慢下来,给线程池争取处理时间。但需注意,这会影响主线程性能。
new ThreadPoolExecutor.CallerRunsPolicy();

// 3. DiscardPolicy - 静默丢弃
// 静默丢弃无法处理的任务,不抛异常也不执行。风险极高,可能导致数据丢失,生产环境慎用。
new ThreadPoolExecutor.DiscardPolicy();

// 4. DiscardOldestPolicy - 丢弃最老任务
// 丢弃队列中最老的任务(最早进入队列的任务),然后尝试重新提交当前任务。
// 适用于新任务比旧任务更重要的场景。
new ThreadPoolExecutor.DiscardOldestPolicy();

// 5. 自定义策略 - 记录日志或持久化
// 实现 RejectedExecutionHandler 接口,可记录日志、持久化到数据库、发送告警或转移到其他队列。
new RejectedExecutionHandler() {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 自定义处理:如写入数据库、发送告警
        log.error("Task rejected: {}", r);
    }
};

七、生产环境最佳实践

不推荐:直接使用 Executors 快捷方法

原因:

  • newFixedThreadPool 和 newSingleThreadExecutor 使用无界队列,任务堆积会导致 OOM
  • newCachedThreadPool 允许创建无限线程,资源耗尽风险极高
  • 隐藏了关键参数,不利于问题排查和性能调优
// 阿里巴巴 Java 开发手册明确禁止
ExecutorService pool = Executors.newFixedThreadPool(100); // 隐藏风险

推荐:手动创建 ThreadPoolExecutor

public class CustomThreadPool {
    
    public static ThreadPoolExecutor createSafePool(
            int coreSize, 
            int maxSize, 
            int queueCapacity) {
        
        return new ThreadPoolExecutor(
            coreSize,
            maxSize,
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(queueCapacity), // 明确有界队列
            new CustomThreadFactory("BizPool"),      // 自定义命名
            new ThreadPoolExecutor.CallerRunsPolicy() // 降级保护
        );
    }
    
    // 自定义线程工厂(便于监控和排查)
    static class CustomThreadFactory implements ThreadFactory {
        private final AtomicInteger counter = new AtomicInteger(1);
        private final String prefix;
        
        CustomThreadFactory(String prefix) {
            this.prefix = prefix;
        }
        
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, prefix + "-" + counter.getAndIncrement());
            t.setDaemon(false);
            t.setUncaughtExceptionHandler((thread, ex) -> {
                log.error("Thread {} exception", thread.getName(), ex);
            });
            return t;
        }
    }
}

动态线程池配置(Spring 环境)

@Configuration
public class ThreadPoolConfig {
    
    @Bean("orderExecutor")
    public ThreadPoolExecutor orderExecutor(
            @Value("${thread.order.core:4}") int core,
            @Value("${thread.order.max:8}") int max,
            @Value("${thread.order.queue:100}") int queue) {
        
        return new ThreadPoolExecutor(
            core, max, 60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(queue),
            new ThreadFactoryBuilder().setNameFormat("order-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
}

八、监控与调优

// 运行时监控指标
ThreadPoolExecutor executor = // ...;

// 当前池大小
int poolSize = executor.getPoolSize();

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

// 任务队列积压
int queued = executor.getQueue().size();

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

// 拒绝任务数(需自定义计数器)

九、总结

Executors 是 ThreadPoolExecutor 的"快捷方式",但生产环境应该绕过它:

1.关系: Executors 是工厂类,ThreadPoolExecutor 是实现类。前者通过静态工厂方法创建后者的实例,但使用了隐藏风险的默认配置。

2.区别: Executors 追求简洁易用,牺牲了可控性;ThreadPoolExecutor 暴露全部参数,要求开发者理解每个配置的含义和影响。

3.选择原则:

  • 学习阶段或单元测试:可以使用 Executors 快速验证
  • 生产环境:必须手动构造 ThreadPoolExecutor,明确指定有界队列、合理的线程数范围和拒绝策略

4.核心原则: 线程池的每个参数都应该是经过计算和压测的,而非依赖默认值。高并发系统的稳定性建立在显式控制之上,而非隐式假设。

"并发编程只有两种难度:难,和非常难。而隐藏细节的 API,往往让问题从'难'变成'非常难'。" —— 在生产环境中,显式优于隐式。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐