Java线程池 超详细零基础完整版笔记(小白易懂+面试必背)
一、线程池是什么?(通俗解释)
我们平时代码中 new Thread() 是临时创建线程、用完就扔。
线程池就是一个“线程复用池子”:提前创建好一批线程,统一管理,任务来了直接用,任务做完线程不销毁、继续等待下一个任务。
Java线程池底层核心实现类:ThreadPoolExecutor,由7大核心参数控制所有运行规则。
线程池七大核心参数(源码+通俗详解)
java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
1. corePoolSize 核心线程数
线程池里常驻的正式员工,默认空闲也不销毁,一直待命处理任务。
2. maximumPoolSize 最大线程数
线程池最多能容纳的总线程数。
总线程 = 核心线程(正式工) + 非核心线程(临时工)
3. keepAliveTime 空闲存活时间
专门给临时非核心线程用的:临时工空闲超过这个时间,直接辞退回收,节省资源。
4. unit 时间单位
配合空闲时间使用:毫秒、秒、分钟。
5. workQueue 阻塞队列
任务排队缓冲区。核心线程忙完了,新任务就先排队进队列。
6. threadFactory 线程工厂
专门用来创建线程,可以自定义线程名称、优先级,方便线上排查BUG。
7. handler 拒绝策略
池子满了、队列也满了,新任务没地方放,触发的兜底处理规则。
记忆口诀:核心最大空闲时,单位队列工厂拒
二、为什么一定要用线程池?(不用的后果)
如果频繁 new Thread() 会出现的问题
1. 非常耗资源:线程创建、销毁是重量级操作,频繁创建会大量消耗CPU、内存,产生大量上下文切换开销。
2. 响应速度慢:每次任务都要新建线程,浪费创建时间。
3. 并发完全不可控:任务多就无限创建线程,极易OOM、CPU打满、服务直接宕机。
4. 无法管理:线程散乱,无法监控、无法统一关闭、出问题无法排查。
使用线程池的四大好处
1. 线程复用:线程用完不销毁,循环干活,大幅节省资源。
2.响应更快:任务直接复用现成线程,不用重新创建。
3. 并发可控:限制最大线程数,保护服务器不会被打崩。
4. 可监控、可扩展:支持排队、限流、拒绝策略、任务监控。
三、线程池完整执行流程(小白必懂、面试必考)
每当我们提交一个新任务,线程池严格按照 4步顺序执行:
第一步:优先创建核心线程
当前工作线程数 < 核心线程数 → 直接新建核心线程执行任务。
第二步:核心线程满了,任务进队列
核心线程全部在忙 → 新任务进入阻塞队列排队。
第三步:队列满了,创建临时线程
队列存不下了 → 创建非核心临时线程处理任务(总数不超过最大线程数)。
第四步:全部满载,触发拒绝策略
核心线程忙、队列满、临时线程也开满了 → 执行拒绝策略。
流程口诀:先开核心,再进队列;队列满了开临时,满载触发拒绝器
线程池四大内置拒绝策略
1. AbortPolicy(默认):直接抛异常,任务失败。
2. DiscardPolicy:默默丢掉新任务,不报错、无日志。
3. DiscardOldestPolicy:丢掉队列里最久没执行的任务,让当前新任务优先执行。
4. CallerRunsPolicy:谁提交的任务,谁自己跑(主线程执行,起到限流作用)。
拒绝策略口诀:默认抛异常,丢弃新任务,丢弃最老的,调用者来跑
四、JDK自带四种预定义线程池(Executors)+ 致命弊端
JDK提供Executors工具类,可以快速创建线程池,但是生产一律禁止使用,全部有坑!
1. newFixedThreadPool(n) 固定线程池
特点:核心线程固定,不会创建临时线程;底层是无界队列。
弊端:任务无限堆积,队列越来越大,直接 OOM内存溢出。
2. newSingleThreadExecutor() 单线程池
特点:全程只有1个线程,任务串行执行。
弊端:同样无界队列,任务堆积严重,极易OOM。
3. newCachedThreadPool() 缓存线程池
特点:无核心线程、最大线程数无限、使用SynchronousQueue不存任务。
弊端:来任务就新建线程,并发高会无限创建线程 → 线程爆炸、CPU飙升、OOM。
4. newScheduledThreadPool() 定时线程池
特点:用来做延迟任务、周期性定时任务。
弊端:最大线程数无上限,高并发存在线程溢出风险。
阿里开发强制规范
禁止使用Executors创建线程池!
必须手动通过 ThreadPoolExecutor 构造方法自定义线程池:手动配置有界队列、合理最大线程数、自定义线程名、自定义拒绝策略,杜绝OOM风险。
五、线程池进阶核心知识点(小白易懂)
1. 核心线程回收机制
默认:核心线程永久存活,空闲也不回收。
如果开启 allowCoreThreadTimeOut(true):核心线程空闲超时也会被回收。
2. 四种阻塞队列作用与区别
ArrayBlockingQueue 有界队列:固定容量,生产推荐使用,可控不OOM。
LinkedBlockingQueue 无界队列:不限制任务数量,无限堆积OOM。
SynchronousQueue 同步队列:不存储任务,来任务必须立刻执行,适合瞬时高并发。
DelayQueue 延迟队列:任务延时执行,适合定时业务。
队列口诀:数组有界防溢出,链表无界易OOM;同步队列不缓存,延迟队列定时取
3. 线程池五大运行状态
RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED
RUNNING:正常运行,接收新任务。
SHUTDOWN:调用shutdown(),不接新任务,做完剩余任务再关闭。
STOP:调用shutdownNow(),不接新任务、中断正在执行的任务、丢弃排队任务。
TIDYING:所有任务结束,线程清空,准备关闭。
TERMINATED:线程池彻底关闭。
状态口诀:运行收任务,停机拒新活;停止断线程,整理终关闭
4. 线程池钩子扩展方法
线程池提供三个空方法,可自定义扩展:
beforeExecute():任务执行前触发
afterExecute():任务执行后触发
terminated():线程池关闭后触发
用途:统计耗时、记录日志、异常监控、资源释放。
5. 线程复用原理
线程执行完任务不会销毁,会循环从阻塞队列take()抢任务;
没有任务时线程阻塞休眠、释放CPU;
非核心线程超时没人用就回收,核心线程永久待命,实现线程复用。
6. execute 和 submit 提交任务区别
execute():只执行任务,异常直接抛出,当前线程销毁,线程池会新建线程补上。
submit():带返回值,异常会封装在Future中,调用get()才会报错,线程不会销毁。
六、线程池线程数配置公式 + 详细设计原理(重点)
1. CPU密集型任务(计算多、IO少)
场景:数据计算、加密、排序、大量运算。
配置公式:核心线程数 = CPU核心数 + 1
原理:充分占满CPU算力,防止线程频繁切换,最大化运算效率。
2. IO密集型任务(数据库、网络、文件请求)
场景:查数据库、调接口、读写文件、业务等待多。
配置经验值:CPU核心数 × 2 | 直接设置 20~50
设计原理(通俗解释):
IO密集型任务,线程大部分时间都在阻塞等待(等数据库返回、等网络响应),基本不占用CPU。
所以可以多开一些线程,让CPU在等待间隙处理更多任务,压榨空闲资源,大幅提升系统吞吐量。
3. 为什么不能设置无限大线程数?
1. 每一个线程都会占用独立栈内存,线程无限多会直接内存溢出OOM。
2. 操作系统切换线程需要上下文切换开销。
线程过多 → CPU一直在切换线程、没时间处理业务 → 吞吐量暴跌、服务器卡死、服务宕机。
七、高频面试简答题(小白背诵版)
1. 核心线程会不会被回收?
默认永久存活,开启allowCoreThreadTimeOut(true)后会超时回收。
2. 为什么要自定义线程名称?
默认线程只有编号,线上CPU飙升、死锁、报错时,无法定位业务;自定义线程名可以快速排查问题。
3. 什么是线程饥饿?怎么解决?
同一线程池中,任务互相等待、全部阻塞,导致部分任务一直得不到执行。
解决:不同业务拆分独立线程池,做好线程池隔离。
4. 线程池怎么监控?
监控活跃线程数、队列堆积数量、已完成任务数,队列堆积过高及时告警、优化慢任务。
更多推荐
所有评论(0)