Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背

文章收录专栏:Java 核心原理全解:源码・并发・面试实战

系列文章:

Java 并发锁机制:从底层原理到锁升级,这一篇就够了

深入理解 Java 内存模型(JMM):从抽象规范到生产实践

Java 线程从生到死:创建三式、六态流转、中断协作与死锁活锁饥饿,一篇讲透

衔接引子 :上一篇讲线程 "怎么创建",但生产环境里我们几乎不直接 new Thread------ 线程创建销毁开销大、无法控制并发数。这一篇讲 JUC 里最核心的线程池 ,以及它带来的经典副作用 ThreadLocal 内存泄漏。

1. 七大参数(面试必背)

corePoolSize 核心线程数、maximumPoolSize 最大线程数、keepAliveTime 空闲存活、TimeUnit 时间单位、BlockingQueue 任务队列、ThreadFactory 线程工厂、RejectedExecutionHandler 拒绝策略。

复制代码
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    2,                                    // 核心线程数
    4,                                    // 最大线程数
    60, TimeUnit.SECONDS,                 // 非核心线程空闲 60s 回收
    new ArrayBlockingQueue<>(100),        // 有界队列
    r -> new Thread(r, "biz-pool-" + new AtomicInteger().incrementAndGet()), // 命名工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

2. 执行流程(精确到 execute)

提交任务 → ① 线程数 < corePoolSize → 建核心线程执行;② 否则尝试入队 offer();③ 入队失败且线程数 < maximumPoolSize → 建非核心线程执行;④ 否则执行拒绝策略。

顺序是 "核心 → 入队 → 非核心 → 拒绝",先判断核心再入队,这是最容易被面试官揪的细节。

3. 生命周期与关闭

状态机:RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED。

  • shutdown():平缓关闭,不再收新任务,执行完队列已有任务
  • shutdownNow():立即关闭,interrupt 所有线程,返回未执行任务列表
  • beforeExecute/afterExecute 钩子可埋点;prestartAllCoreThreads() 预热核心线程

4. 四大拒绝策略

策略 行为 场景
AbortPolicy(默认) 抛 RejectedExecutionException 明确报错
CallerRunsPolicy 提交线程自己执行 降级限流
DiscardOldestPolicy 丢队列最旧,重提当前 可容忍丢任务
DiscardPolicy 静默丢弃 不推荐

生产常用自定义策略:记录日志 + 落库或 MQ 补偿。

5. 内置线程池风险(Executors 陷阱)

  • FixedThreadPool/SingleThreadPool:无界队列,任务堆积 OOM
  • CachedThreadPool:最大线程数无上限,可耗尽 CPU / 内存

生产规范:手动 new ThreadPoolExecutor + 有界队列 + 命名工厂 + 合理拒绝策略 + 按业务拆分。

6. ThreadLocal:原理与内存泄漏

原理:每个 Thread 持有一个 ThreadLocalMap,ThreadLocal 为 key、线程私有值为 value;哈希冲突用开放地址法(线性探测)。

内存泄漏根因:key 是弱引用、GC 后变 null,但 value 是强引用无法回收;线程池复用线程时 value 长期挂留。

复制代码
ThreadLocal<User> tl = new ThreadLocal<>();
try {
    tl.set(new User("木雨"));
    // 业务逻辑
} finally {
    tl.remove(); // 必须在 finally 中清理
}

扩展 :InheritableThreadLocal 子线程创建时继承(线程池复用不传递);TransmittableThreadLocal(TTL,阿里)基于 "捕获→回放→恢复" 解决线程池上下文传递 ------ 微服务链路追踪必备。

7. 线程池死锁(生产高频)

同一个池内:任务 A 提交任务 B 并阻塞等待 B 的结果 ,核心线程被这类等待任务占满,B 永远排不进队 → 线程耗尽、任务堆积。解决:不同层级任务拆分线程池,禁止池内任务互相等待。

金句速记

线程池三问:队列满没满?线程到没到上限?拒绝策略认不认?ThreadLocal 一戒:用完全程 remove,不留垃圾在池里。

相关推荐
小羊没烦恼!4 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里4 天前
java中的继承和多态的区别
java
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕4 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖4 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时4 天前
01-06-A-JVM排查实战详解
java·jvm
晨米酱4 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
vipxieliang4 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi4 天前
mysql 查询所有表名并授权
java