大家好,我是程序员二叉。
简介
线程池是后端开发高频核心考点,本文完整覆盖七大参数、执行流程、四类线程池、四种拒绝策略、阿里规范、核心线程计算、回收关闭、execute/submit差异、异常捕获全知识点。欢迎点赞关注收藏。
一、线程池七大核心参数含义
- corePoolSize 核心线程数
常驻存活的工作线程,正常情况下不会被回收;就算空闲也保留。 - maximumPoolSize 最大线程数
线程池能容纳的总线程上限;最大线程数 = 核心线程 + 非核心临时线程。 - keepAliveTime 非核心线程空闲时长
临时线程空闲超过该时间,会被自动回收;核心线程默认不回收。 - unit 时间单位
配合keepAliveTime,如TimeUnit.SECONDS、MILLISECONDS。 - workQueue 阻塞任务队列
存放等待执行的任务,仅核心线程全部忙碌时,新任务进入队列排队。 - threadFactory 线程工厂
用来创建线程,可以自定义线程名称、优先级、守护线程、异常捕获。 - handler 拒绝策略
当核心线程满、队列满、总线程达到最大值,新任务触发拒绝策略。
二、线程池完整工作执行流程
- 提交任务,判断当前运行线程数小于corePoolSize→ 新建核心线程执行任务
- 核心线程已满,判断阻塞队列是否未满→ 任务放入队列排队
- 队列塞满,判断总线程数小于maximumPoolSize→ 创建临时非核心线程执行任务
- 总线程达到最大值,队列也满 → 执行预设拒绝策略
- 任务执行完毕,临时线程等待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快速创建线程池?
- newFixedThreadPool / newSingleThreadExecutor 使用无界LinkedBlockingQueue
海量任务不断堆积队列,内存持续上涨,最终OOM内存溢出。 - newCachedThreadPool / newScheduledThreadPool 最大线程数为Integer.MAX_VALUE
并发突刺瞬间创建成千上万线程,操作系统线程切换疯狂消耗CPU,极易服务宕机。 - 规范要求:手动使用ThreadPoolExecutor构造器自定义线程池,指定有界队列、合理最大线程、自定义线程名、自定义拒绝策略。
六、核心线程数合理计算公式
1. CPU密集型(大量计算、少IO)
公式:核心线程数 = CPU核心数 + 1
多线程频繁切换损耗大,线程数贴近CPU核数;+1兜底处理偶尔阻塞场景
2. IO密集型(数据库、Redis、网络请求、文件读写)
公式:核心线程数 = CPU核心数 * (1 + 平均IO等待时间 / CPU计算时间)
简易经验值:CPU核数 * 2 ~ CPU核数 * 5;IO阻塞越高,线程数可越大
七、线程池空闲线程回收机制
- 非核心临时线程
空闲时间超过keepAliveTime → 自动销毁回收 - 核心线程默认永不回收
开启allowCoreThreadTimeOut(true)后,核心线程空闲超时同样回收 - 回收逻辑在任务执行完后的自旋循环中判断时间,超时退出线程
八、shutdown() 和 shutdownNow() 关闭线程池区别
1. shutdown()
- 标记线程池为关闭状态,不再接收新任务
- 已提交队列中的任务、正在执行的任务全部执行完成
- 优雅关闭,等待所有任务跑完再终止
- 返回boolean无强制中断
2. shutdownNow()
- 拒绝新任务
- 立刻调用interrupt()中断正在运行的线程
- 返回未执行的任务列表
- 暴力终止,适合紧急停机、故障回滚场景
九、execute() 和 submit() 提交任务区别
execute()
- 只接收Runnable,无返回值
- 任务内部异常直接抛出,线程终止
- 无Future接收结果
submit()
- 支持Runnable、Callable,有返回值Future
- 任务异常会被封装进Future,调用get()才抛出异常
- 可捕获异步执行结果、捕获异常信息
十、线程池三种异常捕获方式
方式1:任务内部try-catch(最推荐)
在run()/call()方法内部包裹全部业务代码,直接捕获异常,避免工作线程死亡。
方式2:submit提交,Future.get()捕获异常
submit返回Future,调用get()时会抛出ExecutionException,内部包裹原始异常。
方式3:自定义ThreadFactory,给线程设置UncaughtExceptionHandler
全局统一捕获线程未处理异常,统一打印日志、告警,适合统一监控。
面试速记总结
- 七大参数:核心线程、最大线程、空闲时间、单位、队列、线程工厂、拒绝策略
- 执行顺序:核心→队列→临时线程→拒绝策略
- Executors四大池均有OOM/线程爆炸隐患,阿里禁止使用
- CPU密集核数+1;IO密集根据阻塞比例放大线程数
- CallerRunsPolicy业务最稳妥;Abort默认抛异常
- shutdown优雅等任务;shutdownNow暴力中断
- execute无返回异常直抛;submit带Future,get才曝异常
- 优先任务内try-catch捕获异常