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,不留垃圾在池里。

相关推荐
杨杨杨大侠3 小时前
都能接模型、RAG 和 MCP,Java Agent 框架到底差在哪?
java
Thinker QAQ4 小时前
并发编程(九):BlockingQueue 与 Channel——语言层的原子性、可见性与有序性
java·python·go·并发编程·channel·blockingqueue·message passing
霸道流氓气质5 小时前
RAG 可观测性与生产监控实战:四层遥测模型、五大仪表盘与 OpenTelemetry 全链路追踪的 Java 生产级实现
java·人工智能·python
AOI小白新手上路5 小时前
autodl常用的tar 打包下载(数据集)命令手册:逐段拆解与套用模板
java·大数据·数据库
dishugj7 小时前
系统架构风险点、敏感点和权衡点的概念和区分
java·开发语言
子林super7 小时前
MySQL三大日志(binlog、redo log和undo log)详解
面试
苜蓿muxu7 小时前
常用代码命名规范
java
马优晨7 小时前
JAVA 中 VO 实体类是什么意思,为什么要做这个
java·开发语言·状态模式·java vo·java vo是什么
Baron X8 小时前
Eclipse Temurin 21 (JRE) Alpine 镜像获取、离线导入与推送操作
java·docker·eclipse·temurin