Java 线程池完整详解:ThreadPoolExecutor 源码、7 大参数、拒绝策略与生产调优最佳实践

0 阅读15分钟

📋 概述

一句话:线程池是"复用线程、控制并发、防止雪崩"的核心基础设施,ThreadPoolExecutor 是其唯一核心实现。

从一个真实问题切入

"阿里 Java 开发手册明确禁止使用 Executors.newFixedThreadPool(),为什么?"

很多同学的第一反应是"无界队列会 OOM"——没错,但这只是冰山一角。真正的问题是:Executors 工厂方法把关键参数隐藏了,你不知道自己创建的线程池有多大、队列有多长、拒绝时会发生什么。一旦流量突增,轻则任务堆积导致内存溢出,重则线程数爆炸拖垮整个服务。

线程池解决的核心问题

问题没有线程池时有线程池时
资源消耗每个任务 new Thread(),创建/销毁开销大线程复用,创建一次跑 N 个任务
响应速度等线程创建才能执行任务提交即调度
并发控制无法限制同时运行的线程数核心线程数 + 最大线程数双重控制
防雪崩任务无限堆积,内存/CPU 耗尽有界队列 + 拒绝策略兜底

mermaid-diagram-2026-08-05-093934.png

💡 生活类比:出租车车队

理解线程池最直观的方式是把它想象成一个出租车公司

mermaid-diagram-2026-08-05-094008.png

出租车公司概念线程池概念类比说明
正式司机corePoolSize(核心线程数)即使没乘客也保留,随时待命
临时司机maximumPoolSize(最大线程数)乘客太多时临时招聘
候客区workQueue(工作队列)正式司机都在忙,乘客先排队
超时解雇keepAliveTime(空闲存活时间)临时司机等太久没活就走人
拒载RejectedExecutionHandler(拒绝策略)车满了、队列满了,新乘客怎么办
司机培训ThreadFactory(线程工厂)决定司机怎么培训、穿什么工服

🔍 ThreadPoolExecutor 7 大核心参数 ⭐⭐

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

参数逐一拆解

参数含义类比注意事项
corePoolSize核心线程数——即使空闲也保留正式司机数量设置后不会自动缩小(需 allowCoreThreadTimeOut
maximumPoolSize最大线程数——核心+非核心总上限司机总数(正式+临时)必须 ≥ corePoolSize
keepAliveTime非核心线程空闲多久后销毁临时司机等多久没活就走仅对非核心线程生效(除非 allowCoreThreadTimeOut(true)
unitkeepAliveTime 的时间单位小时/分钟/秒通常用 TimeUnit.SECONDS
workQueue等待执行的任务队列候客区⚠️ 必须有界,否则 OOM
threadFactory线程创建的工厂人事部门自定义可给线程命名,便于排查
handler队列满+线程满时的拒绝策略拒载方式默认 AbortPolicy(抛异常)

关键设计思想corePoolSizemaximumPoolSize 的分离,让线程池能弹性伸缩——平时用少量核心线程节省资源,高峰时临时扩线程应对突发。

🔍 线程池执行流程 ⭐

当你调用 executor.execute(task) 时,发生了什么?核心原则:先 core → 队列 → max → 拒绝

mermaid-diagram-2026-08-05-094035.png

重点理解先入队,再扩容。很多人以为是"线程满了才入队",实际上线程数达到 corePoolSize 后,新任务会先尝试入队,队列满了才创建非核心线程。这个设计是为了避免频繁创建线程——如果每个任务都创建线程,线程池就退化成 new Thread().start() 了。

// ThreadPoolExecutor.execute() 源码简化(JDK 17)
public void execute(Runnable command) {
    int c = ctl.get();
    // 1. 当前线程数 < corePoolSize → 创建核心线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))  // true=核心线程
            return;
        c = ctl.get();
    }
    // 2. 核心线程满 → 入队
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 2.1 入队成功但线程池已关闭 → 移除任务并拒绝
        if (!isRunning(recheck) && remove(command))
            reject(command);
        // 2.2 入队成功且线程数为0 → 创建非核心线程兜底
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);  // false=非核心线程
    }
    // 3. 入队失败(队列满)→ 尝试创建非核心线程
    else if (!addWorker(command, false))
        // 4. 创建失败 → 拒绝
        reject(command);
}

addWorker(command, core) 的两步

  1. CAS 增加线程计数(workerCountOf(c) + 1
  2. 创建 Worker 对象并启动线程

WorkerThreadPoolExecutor 的内部类,它实现了 Runnable,同时继承了 AQS(用于中断控制)。每个 Worker 包装一个 Threadrun() 方法里循环从队列取任务执行。

🔍 4 种拒绝策略

当线程数达到 maximumPoolSize 且队列已满时,新提交的任务会被拒绝。JDK 内置 4 种策略:

mermaid-diagram-2026-08-05-094054.png

策略行为适用场景代码
AbortPolicy(默认)RejectedExecutionException需要感知拒绝,上层处理new ThreadPoolExecutor.AbortPolicy()
CallerRunsPolicy由提交任务的线程执行不丢任务 + 天然削峰new ThreadPoolExecutor.CallerRunsPolicy()
DiscardPolicy静默丢弃,什么都不做允许丢失(如日志采集)new ThreadPoolExecutor.DiscardPolicy()
DiscardOldestPolicy丢弃队列头部的任务,重试当前任务总是执行最新任务new ThreadPoolExecutor.DiscardOldestPolicy()
// 示例:CallerRunsPolicy 的效果
// 如果线程池满了,任务会在 main 线程(调用者线程)里执行
// 这会"阻塞"调用者,天然起到削峰作用

ExecutorService pool = new ThreadPoolExecutor(
    2, 2, 0, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1),
    new ThreadPoolExecutor.CallerRunsPolicy()  // 调用者执行
);

pool.submit(() -> System.out.println("任务1: " + Thread.currentThread().getName()));
pool.submit(() -> System.out.println("任务2: " + Thread.currentThread().getName()));
pool.submit(() -> System.out.println("任务3: " + Thread.currentThread().getName())); // 队列满,调用者执行

// 预期输出:
// 任务1: pool-1-thread-1
// 任务2: pool-1-thread-2
// 任务3: main  ← 调用者线程执行了

阿里规范推荐:自定义拒绝策略——记录日志 + 报警通知 + 持久化到磁盘。

(r, executor) -> {
    // 1. 记录日志
    log.error("Task rejected, pool: {}, queue: {}/{}",
        executor.toString(),
        ((ArrayBlockingQueue<?>) executor.getQueue()).size(),
        ((ArrayBlockingQueue<?>) executor.getQueue()).remainingCapacity());
    // 2. 报警
    alarmService.alert("线程池任务被拒绝");
    // 3. 持久化(异步)
    asyncPersistenceService.save(((Runnable) r));
}

🔍 BlockingQueue 选型

任务队列是线程池的"缓冲带",选错队列直接影响系统稳定性。

队列数据结构容量特点适用场景
ArrayBlockingQueue数组有界(必须指定)FIFO,一把锁控制推荐,行为可预测
LinkedBlockingQueue链表默认 Integer.MAX_VALUE两把锁(put/take 分离)⚠️ 必须指定大小
SynchronousQueue无存储0(直接交付)不缓存,直接给消费者CachedThreadPool,高吞吐
PriorityBlockingQueue无界优先级排序任务有优先级
DelayQueue无界延迟到期才能取ScheduledThreadPool
// ❌ 危险:无界队列,任务堆积时 OOM
new LinkedBlockingQueue<>()         // 默认 Integer.MAX_VALUE

// ✅ 安全:有界队列
new ArrayBlockingQueue<>(1000)      // 推荐
new LinkedBlockingQueue<>(1000)     // 也可,但需注意两把锁的性能

ArrayBlockingQueue vs LinkedBlockingQueue

对比维度ArrayBlockingQueueLinkedBlockingQueue
底层结构数组链表
锁机制一把锁(put 和 take 互斥)两把锁(put 和 take 独立)
内存分配初始化时一次性分配每次入队分配 Node
GC 压力高(频繁创建 Node)
吞吐量中等高并发时可能更好
推荐度⭐⭐⭐⭐⭐(必须指定容量)

🔍 线程池状态 ⭐

ThreadPoolExecutor 内部用一个 AtomicInteger 类型的 ctl 字段同时表示线程池状态当前线程数

  • 高 3 位:线程池运行状态(RUNNING / SHUTDOWN / STOP / TIDYING / TERMINATED)
  • 低 29 位:当前活跃线程数(最多支持约 5.3 亿个线程)

mermaid-diagram-2026-08-05-094119.png

状态接受新任务处理队列中的任务中断空闲线程说明
RUNNING-1 << 29正常运行,可接受新任务
SHUTDOWN0 << 29调用 shutdown() 后,不接新任务,继续处理队列
STOP1 << 29调用 shutdownNow() 后,不接新任务,中断线程
TIDYING2 << 29所有任务终止,workerCount=0,即将执行 terminated()
TERMINATED3 << 29terminated() 钩子执行完毕
// ctl 字段的位操作
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

// 提取状态:高3位
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 提取线程数:低29位
private static int workerCountOf(int c) { return c & CAPACITY; }

为什么用一个 int 同时存状态和线程数? 因为状态和线程数需要原子性地同时更新——如果分成两个变量,可能出现"状态是 STOP,但线程数还没减"的不一致。一个 int 用 CAS 一次搞定。

💻 可跑代码

Demo 1:手动创建线程池(7 参数)

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        // 手动创建线程池,7 个参数全部显式指定
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,                                    // corePoolSize: 2个核心线程
            4,                                    // maximumPoolSize: 最多4个线程
            60, TimeUnit.SECONDS,                 // 非核心线程空闲60秒后销毁
            new ArrayBlockingQueue<>(3),          // workQueue: 有界队列,容量3
            new ThreadFactory() {                 // threadFactory: 自定义命名
                private final AtomicInteger seq = new AtomicInteger(1);
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "worker-" + seq.getAndIncrement());
                }
            },
            new ThreadPoolExecutor.AbortPolicy()  // handler: 拒绝策略(抛异常)
        );

        // 提交 8 个任务
        for (int i = 1; i <= 8; i++) {
            final int taskId = i;
            try {
                executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName()
                        + " 执行任务 " + taskId);
                    try { Thread.sleep(500); } catch (Exception e) {}
                });
                System.out.println("任务 " + taskId + " 提交成功");
            } catch (RejectedExecutionException e) {
                System.out.println("任务 " + taskId + " 被拒绝: " + e.getMessage());
            }
        }

        executor.shutdown();
        try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch (Exception e) {}
        System.out.println("所有任务完成");
    }
}

// 预期输出(顺序可能不同):
// 任务 1 提交成功
// 任务 2 提交成功
// 任务 3 提交成功
// 任务 4 提交成功
// 任务 5 被拒绝: Task java.util.concurrent.FutureTask rejected from java.util.concurrent.ThreadPoolExecutor
// 任务 6 被拒绝: ...
// 任务 7 被拒绝: ...
// 任务 8 被拒绝: ...
// worker-1 执行任务 1
// worker-2 执行任务 2
// worker-3 执行任务 3
// worker-4 执行任务 4
// worker-1 执行任务 5(队列中取)
// ...
// 所有任务完成

Demo 2:submit 返回 Future

import java.util.concurrent.*;
import java.util.ArrayList;
import java.util.List;

public class ThreadPoolDemo2 {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10)
        );

        // submit 返回 Future
        Future<String> future = executor.submit(() -> {
            Thread.sleep(1000);
            return "任务结果";
        });

        // 阻塞获取结果
        String result = future.get();  // 阻塞直到完成
        System.out.println("结果: " + result);

        // invokeAll:等待所有任务完成
        List<Callable<Integer>> tasks = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            final int num = i;
            tasks.add(() -> {
                Thread.sleep(200);
                return num * 10;
            });
        }
        List<Future<Integer>> futures = executor.invokeAll(tasks);
        for (Future<Integer> f : futures) {
            System.out.print(f.get() + " ");  // 10 20 30 40 50
        }
        System.out.println();

        // invokeAny:任一完成即可
        String winner = executor.invokeAny(tasks);
        System.out.println("最先完成的返回: " + winner);

        executor.shutdown();
    }
}

// 预期输出:
// 结果: 任务结果
// 10 20 30 40 50
// 最先完成的返回: 30(或其他,取决于执行速度)

Demo 3:4 种拒绝策略对比

import java.util.concurrent.*;

public class ThreadPoolDemo3 {
    public static void main(String[] args) {
        // AbortPolicy:抛异常
        testRejectPolicy("AbortPolicy", new ThreadPoolExecutor.AbortPolicy());

        // CallerRunsPolicy:调用者执行
        testRejectPolicy("CallerRunsPolicy", new ThreadPoolExecutor.CallerRunsPolicy());

        // DiscardPolicy:静默丢弃
        testRejectPolicy("DiscardPolicy", new ThreadPoolExecutor.DiscardPolicy());

        // DiscardOldestPolicy:丢弃最老的
        testRejectPolicy("DiscardOldestPolicy", new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    static void testRejectPolicy(String name, RejectedExecutionHandler handler) {
        System.out.println("=== " + name + " ===");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            1, 1, 0, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2),
            handler
        );

        for (int i = 1; i <= 5; i++) {
            final int taskId = i;
            try {
                executor.execute(() -> {
                    try { Thread.sleep(1000); } catch (Exception e) {}
                    System.out.println("  执行: " + Thread.currentThread().getName() + " task-" + taskId);
                });
            } catch (RejectedExecutionException e) {
                System.out.println("  任务 " + taskId + " 被拒绝(抛异常)");
            }
        }

        executor.shutdown();
        try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (Exception e) {}
        System.out.println();
    }
}

// 预期输出(AbortPolicy 会抛异常,其他不会):
// === AbortPolicy ===
//   执行: pool-1-thread-1 task-1
//   执行: pool-1-thread-1 task-2(从队列取)
//   任务 3 被拒绝(抛异常)
//   任务 4 被拒绝(抛异常)
//   任务 5 被拒绝(抛异常)
//
// === CallerRunsPolicy ===
//   执行: pool-1-thread-1 task-1
//   执行: main task-3(调用者线程执行)
//   执行: pool-1-thread-1 task-2
//   执行: main task-4(调用者线程执行)
//   执行: main task-5(调用者线程执行)
//
// === DiscardPolicy ===
//   执行: pool-1-thread-1 task-1
//   执行: pool-1-thread-1 task-2
//   (任务 3/4/5 静默丢弃,无任何输出)
//
// === DiscardOldestPolicy ===
//   执行: pool-1-thread-1 task-1
//   执行: pool-1-thread-1 task-5(丢弃队列头部的 task-2,重试 task-5)

Demo 4:自定义 ThreadFactory(线程命名)

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo4 {
    
    // 自定义线程工厂:命名 + 守护线程 + 优先级
    static class NamedThreadFactory implements ThreadFactory {
        private final String prefix;
        private final boolean daemon;
        private final AtomicInteger seq = new AtomicInteger(1);
        
        public NamedThreadFactory(String prefix, boolean daemon) {
            this.prefix = prefix;
            this.daemon = daemon;
        }
        
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, prefix + seq.getAndIncrement());
            t.setDaemon(daemon);
            t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
    
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new NamedThreadFactory("order-handler-", false)
        );

        // 提交任务,观察线程名
        for (int i = 1; i <= 4; i++) {
            final int id = i;
            executor.submit(() -> {
                System.out.println("任务 " + id + " 在 " 
                    + Thread.currentThread().getName() + " 上执行");
            });
        }

        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
    }
}

// 预期输出:
// 任务 1 在 order-handler-1 上执行
// 任务 2 在 order-handler-2 上执行
// 任务 3 在 order-handler-1 上执行
// 任务 4 在 order-handler-2 上执行

Demo 5:线程池监控

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo5 {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadFactory() {
                private final AtomicInteger seq = new AtomicInteger(1);
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "mon-" + seq.getAndIncrement());
                }
            }
        );

        // 提交任务模拟负载
        for (int i = 0; i < 15; i++) {
            final int id = i;
            try {
                executor.execute(() -> {
                    try { Thread.sleep(500); } catch (Exception e) {}
                });
            } catch (RejectedExecutionException e) {
                System.out.println("任务 " + id + " 被拒绝");
            }
        }

        // 监控指标
        Thread.sleep(200);
        System.out.println("===== 线程池监控 =====");
        System.out.println("当前线程数:     " + executor.getPoolSize());
        System.out.println("活跃线程数:     " + executor.getActiveCount());
        System.out.println("队列大小:       " + executor.getQueue().size());
        System.out.println("队列剩余容量:   " + executor.getQueue().remainingCapacity());
        System.out.println("已完成任务数:   " + executor.getCompletedTaskCount());
        System.out.println("总任务数:       " + executor.getTaskCount());
        System.out.println("历史最大线程数: " + executor.getLargestPoolSize());
        System.out.println("是否已关闭:     " + executor.isShutdown());
        System.out.println("是否已终止:     " + executor.isTerminated());

        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS);
    }
}

// 预期输出:
// 任务 11 被拒绝
// 任务 12 被拒绝
// 任务 13 被拒绝
// 任务 14 被拒绝
// ===== 线程池监控 =====
// 当前线程数:     4
// 活跃线程数:     4
// 队列大小:       10
// 队列剩余容量:   0
// 已完成任务数:   0
// 总任务数:       15
// 历史最大线程数: 4
// 是否已关闭:     false
// 是否已终止:     false

⚠️ 常见问题与踩坑

Q1:Executors.newFixedThreadPool() 有什么问题?

:内部用 LinkedBlockingQueue<>()(默认容量 Integer.MAX_VALUE),等同于无界队列。任务堆积时不会触发拒绝策略,只会不断往内存里塞,最终 OOM。同理,newSingleThreadExecutor() 也是无界队列。

// ❌ 源码:Executors.newFixedThreadPool()
return new ThreadPoolExecutor(nThreads, nThreads,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());  // 无界队列!

// ✅ 手动创建
new ThreadPoolExecutor(nThreads, nThreads,
    0L, TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<>(1000));  // 有界队列

Q2:Executors.newCachedThreadPool() 有什么问题?

corePoolSize=0maximumPoolSize=Integer.MAX_VALUE,用 SynchronousQueue。每个任务都会创建新线程,高并发时线程数暴增,CPU 和内存都会被打爆。

Q3:核心线程数怎么算?

:根据任务类型区分:

任务类型公式示例
CPU 密集型N + 1(N=CPU 核数)8 核 → 9 个线程
IO 密集型N × 2N / (1 - W)(W=IO 等待占比)8 核,W=0.6 → 20 个线程
混合型拆分为两个独立线程池CPU 池 + IO 池

注意:这只是起点,实际要通过压测调整。Runtime.getRuntime().availableProcessors() 获取 CPU 核数。

Q4:为什么"先入队再扩容"而不是"先扩容再入队"?

:这是 ThreadPoolExecutor核心设计决策。如果先创建非核心线程再入队,每个任务都会触发线程创建(退化成 new Thread().start())。先入队让已有线程有机会复用,只有队列满了才创建新线程——线程复用最大化

Q5:线程池状态流转的触发条件?

  • shutdown() → 进入 SHUTDOWN:不接新任务,处理完队列
  • shutdownNow() → 进入 STOP:不接新任务,中断所有线程
  • 队列空 + workerCount=0 → 进入 TIDYING:所有任务完成
  • terminated() 钩子执行完 → 进入 TERMINATED

Q6:为什么不用 Executors 创建线程池(阿里规范)?

:阿里规范的完整理由:

  1. FixedThreadPool / SingleThreadPool:无界队列,OOM
  2. CachedThreadPool:线程数无上限,CPU/内存爆炸
  3. 无法设置拒绝策略:Executors 工厂方法无法传入自定义 handler
  4. 无法给线程命名:默认线程名 pool-N-thread-M,排查问题困难

Q7:execute() 和 submit() 有什么区别?

对比execute(Runnable)submit(Callable/Runnable)
返回值voidFuture<T>
异常处理直接抛出(未捕获异常处理器)异常封装在 Future 里,调 get() 时抛出
取消任务不支持future.cancel(true)
获取结果不支持future.get() 阻塞获取

Q8:shutdown() 和 shutdownNow() 怎么选?

  • 优雅关闭(推荐):shutdown() + awaitTermination(timeout) → 超时后 shutdownNow()
  • 立即关闭shutdownNow() 中断所有线程,返回未执行的任务列表
  • 关键区别shutdown()处理完队列里的任务shutdownNow() 直接中断
executor.shutdown();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
    List<Runnable> pending = executor.shutdownNow();
    System.out.println("强制关闭," + pending.size() + " 个任务未执行");
}

Q9:线程池如何动态调整参数?

:JDK 17 支持运行时调整,无需重启:

executor.setCorePoolSize(16);           // 调大核心线程数
executor.setMaximumPoolSize(32);        // 调大最大线程数
executor.setKeepAliveTime(120, TimeUnit.SECONDS);

但注意:不能减少 corePoolSize(如果当前线程数 > 新值,不会自动缩减)。

Q10:线程池任务嵌套会导致死锁吗?

:会。如果线程池 A 的任务里提交任务到同一个线程池 A,当所有线程都在等内部任务完成时,就会死锁。解决方案:不同业务用不同线程池隔离。

🎯 最佳实践

Do / Don't 对照表

✅ Do❌ Don't
手动创建 ThreadPoolExecutorExecutors.newXxxThreadPool()
指定有界队列 ArrayBlockingQueue用无界 LinkedBlockingQueue<>()
自定义 ThreadFactory 给线程命名用默认工厂,线程名 pool-N-thread-M
设置合理的拒绝策略只用默认的 AbortPolicy
不同业务用不同线程池所有任务共用一个线程池
shutdown() + awaitTermination() 关闭直接 shutdownNow() 或不关闭
监控 getActiveCount/getQueue().size()不关心线程池状态

阿里规范速查

  1. 线程池必须用 ThreadPoolExecutor 创建,不允许用 Executors
  2. 线程池命名业务名-线程池用途-N,如 order-pool-8
  3. 拒绝策略:不能丢弃的任务用 CallerRunsPolicy,可丢弃的用自定义策略(日志+报警)
  4. 线程池隔离:不同业务独立线程池,避免互相影响
  5. 线程数:CPU 密集型 N+1,IO 密集型 2N,压测验证后调整

💡 面试要点

  1. 线程池 7 大参数:corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(空闲存活时间)、unit(时间单位)、workQueue(工作队列)、threadFactory(线程工厂)、handler(拒绝策略)
  2. 执行流程:先 core → 队列 → max → 拒绝。注意是"先入队再扩容"
  3. 4 种拒绝策略:AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢弃最老)
  4. 为什么禁用 Executors:FixedThreadPool 无界队列 OOM,CachedThreadPool 线程数无上限
  5. 线程数设置:CPU 密集 N+1,IO 密集 2N,混合型拆分为两个线程池
  6. ctl 字段:AtomicInteger,高 3 位存状态,低 29 位存线程数
  7. 线程池 5 种状态:RUNNING → SHUTDOWN → TIDYING → TERMINATED(STOP 由 shutdownNow 触发)
  8. Worker 内部类:继承 AQS,实现 Runnable,封装 Thread + firstTask
  9. execute vs submit:execute 无返回值异常直接抛,submit 返回 Future 异常封装在 Future 里
  10. 关闭线程池shutdown() + awaitTermination() 组合,超时后 shutdownNow()

📝 总结

核心知识点一句话记忆
线程池价值复用线程、控并发、防雪崩
7 大参数core / max / keepAlive / unit / queue / factory / handler
执行流程先 core → 队列 → max → 拒绝
拒绝策略Abort 抛异常、CallerRuns 调用者执行、Discard 丢弃、DiscardOldest 丢最老
Executors 风险无界队列 OOM、线程数无上限、无法自定义
状态机RUNNING → SHUTDOWN → TIDYING → TERMINATED
线程数设置CPU 密集 N+1,IO 密集 2N
最佳实践手动创建、有界队列、线程命名、业务隔离

一句话心法:线程池不是万能药,参数设错比不用还危险——核心数 × 2 + 有界队列 + 合理拒绝是起步配置,压测才是最终答案。

📖 外部参考