1. ThreadPoolExecutor 的七大参数?

构造参数全解
new ThreadPoolExecutor(
    corePoolSize,        1️⃣ 核心线程数:常驻,即使空闲也不回收(allowCoreThreadTimeOut 可改)
    maximumPoolSize,     2️⃣ 最大线程数:核心+非核心的上限
    keepAliveTime,       3️⃣ 非核心线程空闲存活时间
    TimeUnit,            4️⃣ 时间单位
    workQueue,           5️⃣ 任务队列:核心线程满后任务先排队
    threadFactory,       6️⃣ 线程工厂:命名、守护线程等
    handler              7️⃣ 拒绝策略:队列满 + 线程满时
)

执行流程(背下来):

  1. 提交任务 → 核心线程未满 → 创建核心线程执行
  2. 核心线程满 → 任务放入队列排队
  3. 队列满 → 创建非核心线程(最多到 maximumPoolSize)
  4. 线程全满且队列满 → 执行拒绝策略

🎯 面试要点

  • 先排队再扩线程——这是线程池"缓冲优先"的设计(区别于信号量)
  • 线程命名规范:custom-线程工厂,排障时 jstack 一眼认出业务线程
  • 核心线程的懒启动:首次提交任务才创建(prestartAllCoreThreads 可预启动)

2. 四种拒绝策略?

🎯 面试要点

  • 业务上拒绝意味着"过载",建议配套监控报警 + 落库补偿
  • 可以自定义 RejectedExecutionHandler(写日志、入死信队列)

3. Executors 提供的常见线程池?为什么不推荐?

阿里规约明确禁止 Executors 直接创建:无界队列/无限线程数都是隐患,必须手动 new ThreadPoolExecutor 并明确参数。

推荐的规范写法
// 有界队列 + 命名工厂 + CallerRuns 兜底
private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(
    8, 16, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),
    new ThreadFactoryBuilder()     // Guava 或自定义
        .setNameFormat("order-pool-%d").build(),
    new ThreadPoolExecutor.CallerRunsPolicy());

🎯 面试要点

  • 阿里规约三句:不用 Executors、用有界队列、给线程命名
  • ScheduledThreadPool 底层是 DelayedWorkQueue(堆结构)

4. 线程池大小怎么设置?

经验值只是起点,最终要靠压测调整:观察线程利用率(CPU 使用率)和队列积压。

🎯 面试要点

  • 区分"调参公式"与"压测验证",答公式 + 补充"需压测"是加分项
  • IO 密集任务(DB/Redis/HTTP)为主的后端服务:核心线程 2N~4N 常见(N=核数)
  • 动态调整:ThreadPoolExecutor 支持 setCorePoolSize 运行时修改

5. 线程池的常见坑?

优雅关闭模板
pool.shutdown();                          // 停止接收新任务
try {
    if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
        pool.shutdownNow();                // 超时强制中断
        if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
            System.err.println("线程池未能完全停止");
        }
    }
} catch (InterruptedException e) {
    pool.shutdownNow();
    Thread.currentThread().interrupt();
}

🎯 面试要点

  • execute 丢异常 vs submit 捕获异常:面试官爱问"线程池里任务抛异常会怎样"
  • 监控:活跃线程数、队列深度、拒绝次数——阿里建议线程池打点上报

🎤 常见面试追问

  1. 线程池的执行流程(背下来)?——提交任务 → 核心线程未满则新建执行 → 满了进队列 → 队列满创建非核心线程(上限 max)→ 全满执行拒绝策略。
  2. 为什么不推荐 Executors 创建线程池?——newFixedThreadPool 用无界队列(任务无限堆积内存耗尽)、newCachedThreadPool 最大线程数 Integer.MAX(线程爆炸)。必须手动 new ThreadPoolExecutor 明确参数。
  3. 核心线程数怎么定?——CPU 密集 ≈ 核数+1;IO 密集 ≈ 核数 × (1 + 等待/计算时间) 或 2N。经验值只是起点,最终压测验证。
  4. execute 和 submit 的区别?——execute 无返回值,异常直接抛线程打印;submit 返回 Future 可拿结果,异常在 future.get() 时抛——submit 能感知任务异常。
  5. 线程池里的任务抛异常会怎样?——execute 提交:线程被回收重建(Worker 退出),异常只打印不向上抛;submit:异常存在 Future 里。业务任务最好自己 try-catch。

📖 名词解释(本页术语)

术语 大白话解释
ThreadPoolExecutor线程池的实现类。七大参数:核心线程数、最大线程数、空闲存活时间、单位、任务队列、线程工厂、拒绝策略。
核心线程 / 非核心线程核心线程常驻不回收(除非 allowCoreThreadTimeOut);非核心线程空闲超过 keepAliveTime 被回收。
任务队列(workQueue)核心线程满后任务先排队。有界(ArrayBlockingQueue,推荐)vs 无界(LinkedBlockingQueue,危险)。
拒绝策略线程满+队列满时的处理:AbortPolicy 抛异常(默认)/ CallerRunsPolicy 调用线程自己跑(推荐)/ DiscardPolicy 静默丢 / DiscardOldestPolicy 丢最旧。
ExecutorService线程池的接口(ThreadPoolExecutor 实现它),submit/execute/shutdown 等方法。
Executors 工具类快速创建线程池的工厂:Fixed/Cached/Single/Scheduled——方便但有隐患,阿里规约禁止直接用。
shutdown vs shutdownNowshutdown 优雅关闭(拒新任务、执行完存量);shutdownNow 立即中断所有线程并返回未执行任务。下线要用 shutdown + awaitTermination。
CallerRunsPolicy拒绝策略之一:任务由"提交它的线程"自己执行——天然限流(提交方被拖慢)且不丢任务。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。