1) 基础概念速览

  • 线程池:线程复用容器,按需/预先创建线程复用执行任务。

  • 优势:降低创建/销毁成本;受控并发;排队与拒绝;可观测与治理。

  • 核心类ThreadPoolExecutor;常见池:Fixed / Cached / Single / Scheduled

  • 规范:避免直接用 Executors(默认无界/新建线程,易 OOM)。

七个核心参数
corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandler


2) 执行与状态原理

调度路径(四步)

  1. < core → 新建核心线程直接执行

  2. ≥ core入队

  3. 队列满 且 < max → 新建非核心线程

  4. 否则 → 拒绝策略

状态机
RUNNING → SHUTDOWN/STOP → TIDYING → TERMINATED

  • shutdown():温和关闭(不接新单,跑完队列)

  • shutdownNow():立刻关闭(中断 + 返回未执行任务)

Worker 复用与 AQS

  • Worker 执行完首任务后循环 getTask() 复用。

  • Worker 继承 AQS 做“忙闲锁”:忙时持锁,空闲无锁 → 只中断空闲线程。

核心 vs 非核心

  • 非核心空闲超时回收;核心默认常驻(可 allowCoreThreadTimeOut(true) 回收)。


3) 队列 & 拒绝策略

队列

  • ArrayBlockingQueue有界、数组;推荐默认。

  • LinkedBlockingQueue:默认无界,必须限容量

  • SynchronousQueue:直传,不排队,利于快速扩到 max

  • PriorityBlockingQueue:按优先级(注意避免饿死)。

拒绝策略(4)

  • Abort(默认抛异常)

  • CallerRuns(回压上游)

  • Discard(丢新)

  • DiscardOldest(丢最旧)

自定义思路:日志 + 告警 +(可选)落盘/队列重试/降级处理。


4) 实战配置 & 监控排查

参数建议

  • 核心:CPU 密集≈CPU/CPU+1;IO 密集≈2×CPU

  • 最大:核心的 2~5×(结合时延/切换成本压测微调)。

  • 队列:有界;粗略:容量 ≈ QPS × 平均耗时 × 2

监控指标
活跃线程、队列长度、完成数、拒绝数、P95/P99、池状态。
Micrometer → Prometheus/Grafana 告警。

常见问题 & 排查

  • OOM:无界队列/对象泄漏 → 限容量 + 堆分析。

  • 慢/阻塞jstack 定位锁/IO;外部调用设超时。

  • 拒绝多:调大容量/限流/降级。


5) Tomcat 线程池 vs JDK 线程池

  • JDK先排队后扩线程(稳内存)。

  • Tomcat先扩线程到 max 再排队(快出活,适合 Web;关键在自定义 TaskQueue#offer)。

  • 选型:请求链路 → Tomcat Executor;业务异步/批处理 → JDK 池(可多池隔离)


6) OOM 高发场景与真实案例

6.1 突发流量 + 配置不合理

  • QPS 暴涨;core/max 偏小 + 队列无界/过大 → 无限堆积 → OOM。

6.2 消费阻塞(第三方慢/锁/IO 卡住)

  • 线程长期占用,消费速率骤降;队列“温水煮青蛙式”堆积 → OOM。

6.3 任务对象泄漏 + 堆积

  • 任务持有未释放资源(FileInputStream/Process/ByteBuffer 等),少量堆积即可吃光内存。

6.4 框架隐式线程池

  • @Async、MQ 消费者、XXL-Job/Quartz 内部池若无界 → 同样堆积 OOM。


7) 系统性优化方案(落地清单)

  1. 有界队列 + 明确拒绝

    RejectedExecutionHandler handler = (r, ex) -> {
    log.error("Pool full: pool={}, active={}, queue={}",
    ex.getPoolSize(), ex.getActiveCount(), ex.getQueue().size());
    // 返回友好提示/HTTP 503 + 告警
    };
  2. 限流控产:网关/Sentinel;MQ 限速/限批拉取;活动期限流降级。

  3. 超时与取消:HTTP/RPC 设 connect/read 超时;Future.get(timeout) + cancel(true)

    Future f = executor.submit(task);
    try { f.get(3, TimeUnit.SECONDS); }
    catch (TimeoutException e) { f.cancel(true); }
  4. 拆池隔离:按任务类型分池(下游慢不拖垮全局)。

  5. 监控告警:活跃、队列、拒绝、P99、堆使用率;阈值 ≥80% 告警。


8) 口诀与速记

  • 调度四步:先核心排队扩最大拒绝

  • 配置三件有界队列明确拒绝监控到位

  • 避坑两条:别用无界队列;别让外部调用无超时


9) 面试速问速答(精选)

  • execute vs submit:无返回 vs Future(异常在 get() 时抛)。

  • shutdown vs shutdownNow:温和 vs 立刻(中断 + 返回未执行)。

  • 优先级任务:PriorityBlockingQueue + Comparable(注意饥饿)。

  • 取消任务:未执行→remove(task);已执行→任务内响应中断/Future.cancel(true)


结论

限产(限流)限容(有界队列)限时(超时/取消)可观测(监控告警),再配合合理扩缩容任务隔离,线程池才能

posted on 2025-09-15 21:01  lxjshuju  阅读(8)  评论(0)    收藏  举报