--- title: "03-线程池" created: 2026-09-01 tags: - Java --- # 线程池 > [!note] 本篇定位 > 并发面试**出镜率最高**的一篇。主线三件事:**为什么要用线程池**(复用降低开销、统一管理防资源耗尽)、**七个核心参数 + 提交执行流程**(必考画图)、**为什么禁用 Executors 快捷方法**(阿里规约高频题)。参数怎么配、常见队列有哪些,文末给速查。 ## 为什么需要线程池 - **降低资源消耗**:线程的创建和销毁都涉及系统调用与内存分配,复用线程摊薄成本; - **提高响应速度**:任务到达直接复用已有线程,省去创建等待; - **统一管理**:防止无限制 new Thread 把系统线程数打爆,还能提供排队、监控、超时等治理能力。 ## 标准创建:ThreadPoolExecutor 七参数 ```java ExecutorService pool = new ThreadPoolExecutor( 2, // corePoolSize 核心线程数:常驻,不回收(默认) 4, // maximumPoolSize 最大线程数:核心+临时上限 60, TimeUnit.SECONDS, // keepAliveTime + unit 临时线程空闲多久回收 new LinkedBlockingQueue<>(100), // workQueue 任务排队队列 Executors.defaultThreadFactory(),// threadFactory 建线程的工厂(自定义名字方便排查) new ThreadPoolExecutor.AbortPolicy()); // handler 队列满+线程满时的拒绝策略 ``` ## 提交任务后的执行流程(必考画图题) ```text 提交任务 │ ▼ 核心线程未满?──是──▶ 创建核心线程执行 │ 否 ▼ 队列没满?────是──▶ 任务入队等待 │ 否 ▼ 最大线程未满?──是──▶ 创建临时线程执行 │ 否 ▼ 执行拒绝策略 ``` **注意顺序坑**:是**先入队,队列满了才开临时线程**,不是先开满线程再排队——很多人栽在这里。 ## 四种拒绝策略 | 策略 | 行为 | | --- | --- | | AbortPolicy(默认) | 抛 `RejectedExecutionException` | | CallerRunsPolicy | 谁提交谁自己执行——天然反压,常用于业务池 | | DiscardPolicy | 静默丢弃新任务 | | DiscardOldestPolicy | 丢掉队首最老任务,给新任务腾位 | ## 为什么禁用 Executors 的快捷方法(阿里规约) ```java // ❌ 都是坑 Executors.newFixedThreadPool(10); // 队列是无界 LinkedBlockingQueue —— 任务堆积直接 OOM Executors.newSingleThreadExecutor(); // 同上,无界队列 Executors.newCachedThreadPool(); // 最大线程数是 Integer.MAX_VALUE —— 线程无限增长 OOM // ✅ 手动 new ThreadPoolExecutor,明确每个参数 ``` ## 参数怎么设(经验值 + 原理) - **CPU 密集型**(计算、加解密):核心线程数 ≈ **N_CPU + 1**; - **IO 密集型**(RPC 调用、数据库、文件):核心线程数 ≈ **N_CPU × (1 + 等待时间/计算时间)**,粗略按 2N 起步压测调整; - 队列:有界!容量按可容忍的积压量定,并配好监控; - 更科学的做法:压测 + 监控(活跃线程数、队列水位、拒绝次数),参数不是拍出来的。 ## submit vs execute / 优雅关闭 ```java pool.execute(runnable); // 无返回值,异常直接打印堆栈 Future f = pool.submit(callable); // 有返回值,异常在 get() 时抛出 pool.shutdown(); // 不接新任务,已提交的跑完 pool.awaitTermination(30, TimeUnit.SECONDS); // 等待收尾 pool.shutdownNow(); // 中断正在执行的任务(慎用) ``` **坑**:submit 的任务里抛了异常但你不调 `get()`,异常会被**吞掉**——日志里什么都看不到。处理:任务内部 try-catch,或包一层异常记录。 ## 其他要点速记 - 核心线程默认不回收,`allowCoreThreadTimeOut(true)` 可让核心线程也走 keepAlive 回收; - 线程池的线程默认都是**非守护线程**,主线程退出不影响它们,忘记 shutdown 会导致 JVM 不退出; - 动态调参:`setCorePoolSize()` 运行时可改(美团的动态线程池实践就是基于此 + 监控)。 --- ⬅️ [[02-synchronized与volatile|02-synchronized与volatile]] 🏠 [[00-Java|00-Java]] ➡️ [[04-Lock与AQS|04-Lock与AQS]]