【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理

大家好,我是程序员二叉。


简介

线程池是后端开发高频核心考点,本文完整覆盖七大参数、执行流程、四类线程池、四种拒绝策略、阿里规范、核心线程计算、回收关闭、execute/submit差异、异常捕获全知识点。欢迎点赞关注收藏。


一、线程池七大核心参数含义

  1. corePoolSize 核心线程数
    常驻存活的工作线程,正常情况下不会被回收;就算空闲也保留。
  2. maximumPoolSize 最大线程数
    线程池能容纳的总线程上限;最大线程数 = 核心线程 + 非核心临时线程。
  3. keepAliveTime 非核心线程空闲时长
    临时线程空闲超过该时间,会被自动回收;核心线程默认不回收。
  4. unit 时间单位
    配合keepAliveTime,如TimeUnit.SECONDS、MILLISECONDS。
  5. workQueue 阻塞任务队列
    存放等待执行的任务,仅核心线程全部忙碌时,新任务进入队列排队。
  6. threadFactory 线程工厂
    用来创建线程,可以自定义线程名称、优先级、守护线程、异常捕获。
  7. handler 拒绝策略
    当核心线程满、队列满、总线程达到最大值,新任务触发拒绝策略。

二、线程池完整工作执行流程

  1. 提交任务,判断当前运行线程数 小于corePoolSize → 新建核心线程执行任务
  2. 核心线程已满,判断阻塞队列是否未满 → 任务放入队列排队
  3. 队列塞满,判断总线程数小于maximumPoolSize → 创建临时非核心线程执行任务
  4. 总线程达到最大值,队列也满 → 执行预设拒绝策略
  5. 任务执行完毕,临时线程等待keepAliveTime,无新任务则回收;核心线程持续待命

三、JDK内置四大线程池种类与特点

1. Executors.newFixedThreadPool(n)

  • 核心=最大=n,无非核心线程;无界LinkedBlockingQueue
  • 特点:线程数量固定,负载平稳
  • 坑:队列无界,大量任务堆积极易OOM

2. Executors.newCachedThreadPool()

  • 核心线程0,最大Integer.MAX_VALUE;SynchronousQueue无缓冲队列
  • 特点:任务来了立刻开临时线程,空闲60s自动回收
  • 坑:线程无上限,高并发瞬间创建海量线程,CPU、内存打爆

3. Executors.newSingleThreadExecutor()

  • 核心、最大都是1,单线程串行执行;无界队列
  • 特点:所有任务顺序执行
  • 坑:无界队列堆积任务OOM;单点故障阻塞全部任务

4. Executors.newScheduledThreadPool(n)

  • 支持定时、周期性延时任务;可指定核心线程数,最大无上限
  • 适用:定时心跳、定时清理、定时同步
  • 坑:最大线程无限,任务阻塞会疯狂新建线程

四、四种拒绝策略及适用场景

1. AbortPolicy(默认)

直接抛出RejectedExecutionException异常,中断任务提交

适用:重要任务,不允许丢失,必须感知失败异常

2. DiscardPolicy

默默丢弃当前新来的任务,无任何日志无异常

适用:无关紧要、可丢弃的弱实时日志、监控埋点

3. DiscardOldestPolicy

丢弃队列最头部排队最久的旧任务,尝试放入新任务

适用:实时性优先,旧任务过期无用(如实时点位推送)

4. CallerRunsPolicy

不新开线程,交给提交任务的调用线程去执行

适用:流量削峰,减缓提交速度,保障任务不丢失(阿里推荐业务首选)


五、阿里开发规范:为什么禁止使用Executors快速创建线程池?

  1. newFixedThreadPool / newSingleThreadExecutor 使用无界LinkedBlockingQueue
    海量任务不断堆积队列,内存持续上涨,最终OOM内存溢出。
  2. newCachedThreadPool / newScheduledThreadPool 最大线程数为Integer.MAX_VALUE
    并发突刺瞬间创建成千上万线程,操作系统线程切换疯狂消耗CPU,极易服务宕机。
  3. 规范要求:手动使用ThreadPoolExecutor构造器自定义线程池,指定有界队列、合理最大线程、自定义线程名、自定义拒绝策略。

六、核心线程数合理计算公式

1. CPU密集型(大量计算、少IO)

公式:核心线程数 = CPU核心数 + 1

多线程频繁切换损耗大,线程数贴近CPU核数;+1兜底处理偶尔阻塞场景

2. IO密集型(数据库、Redis、网络请求、文件读写)

公式:核心线程数 = CPU核心数 * (1 + 平均IO等待时间 / CPU计算时间)

简易经验值:CPU核数 * 2 ~ CPU核数 * 5;IO阻塞越高,线程数可越大


七、线程池空闲线程回收机制

  1. 非核心临时线程
    空闲时间超过keepAliveTime → 自动销毁回收
  2. 核心线程默认永不回收
    开启allowCoreThreadTimeOut(true)后,核心线程空闲超时同样回收
  3. 回收逻辑在任务执行完后的自旋循环中判断时间,超时退出线程

八、shutdown() 和 shutdownNow() 关闭线程池区别

1. shutdown()

  1. 标记线程池为关闭状态,不再接收新任务
  2. 已提交队列中的任务、正在执行的任务全部执行完成
  3. 优雅关闭,等待所有任务跑完再终止
  4. 返回boolean无强制中断

2. shutdownNow()

  1. 拒绝新任务
  2. 立刻调用interrupt()中断正在运行的线程
  3. 返回未执行的任务列表
  4. 暴力终止,适合紧急停机、故障回滚场景

九、execute() 和 submit() 提交任务区别

execute()

  1. 只接收Runnable,无返回值
  2. 任务内部异常直接抛出,线程终止
  3. 无Future接收结果

submit()

  1. 支持Runnable、Callable,有返回值Future
  2. 任务异常会被封装进Future,调用get()才抛出异常
  3. 可捕获异步执行结果、捕获异常信息

十、线程池三种异常捕获方式

方式1:任务内部try-catch(最推荐)

在run()/call()方法内部包裹全部业务代码,直接捕获异常,避免工作线程死亡。

方式2:submit提交,Future.get()捕获异常

submit返回Future,调用get()时会抛出ExecutionException,内部包裹原始异常。

方式3:自定义ThreadFactory,给线程设置UncaughtExceptionHandler

全局统一捕获线程未处理异常,统一打印日志、告警,适合统一监控。


面试速记总结

  1. 七大参数:核心线程、最大线程、空闲时间、单位、队列、线程工厂、拒绝策略
  2. 执行顺序:核心→队列→临时线程→拒绝策略
  3. Executors四大池均有OOM/线程爆炸隐患,阿里禁止使用
  4. CPU密集核数+1;IO密集根据阻塞比例放大线程数
  5. CallerRunsPolicy业务最稳妥;Abort默认抛异常
  6. shutdown优雅等任务;shutdownNow暴力中断
  7. execute无返回异常直抛;submit带Future,get才曝异常
  8. 优先任务内try-catch捕获异常
相关推荐
大可-5 小时前
Go Air 热重载安装配置指南
开发语言·后端·golang
2401_894915535 小时前
Geo 优化源码部署避坑指南:解决访问异常、定位失效、收录卡顿问题
java·服务器·后端·缓存·开源
Dovis(誓平步青云)5 小时前
《 固井工程软件 Cemsol 的数据管理与国产化适配实践》
android·java·开发语言·人工智能
大模型码小白5 小时前
AI安全前沿:AI大模型安全防护的前沿技术
java·网络·人工智能·python·深度学习·学习·安全
evans在进步5 小时前
LeetCode 34:在排序数组中查找元素的首尾位置——Java 两次二分查找详解
java·python·leetcode
@卓越俊逸_角立杰出@6 小时前
java实现Agent+ReAct demo(Spring Boot + Spring AI Alibaba ReAct Agent)
java·spring·react.js
看浪的路人6 小时前
第6讲:SQL 解析器
java·开发语言·数据库
weixin_440784116 小时前
【OkHttp实现原理】
android·java·okhttp
a3535413826 小时前
C++项目如何架构优化
java·c++·架构
AC赳赳老秦6 小时前
官方技术文档聚合实践:用 OpenClaw 批量抓取开源项目文档,构建离线可检索技术知识库
java·运维·服务器·python·信息可视化·deepseek·openclaw