第一部分:线程池基础
理解线程池的核心概念与优势
学习目标
掌握线程池的作用、优势及适用场景
线程创建问题
直接创建线程的缺点:
- 频繁创建销毁开销大
- 资源耗尽风险
- 线程管理困难
- 无法控制并发数量
线程池优势
线程池解决的核心问题:
- 复用线程资源
- 控制并发数量
- 统一管理线程
- 提供任务队列
线程池复用线程资源,避免频繁创建销毁

线程池核心组件:核心线程、任务队列、非核心线程、拒绝策略
第二部分:线程池创建方式
掌握ExecutorService的多种创建方式
学习目标
理解Executors工厂类与ThreadPoolExecutor构造方法
1. Executors工厂方法
使用Executors提供的工厂方法创建线程池
2. ThreadPoolExecutor构造方法
直接使用ThreadPoolExecutor创建线程池
创建方法 | 线程池类型 | 核心线程数 | 最大线程数 | 队列类型 | 适用场景 |
---|---|---|---|---|---|
newFixedThreadPool | 固定大小 | n | n | LinkedBlockingQueue | 负载较重服务器 |
newSingleThreadExecutor | 单线程 | 1 | 1 | LinkedBlockingQueue | 任务顺序执行 |
newCachedThreadPool | 可缓存 | 0 | Integer.MAX_VALUE | SynchronousQueue | 短时异步任务 |
newScheduledThreadPool | 定时任务 | n | Integer.MAX_VALUE | DelayedWorkQueue | 定时/周期性任务 |
选择建议
1. 生产环境推荐使用ThreadPoolExecutor构造方法
2. 避免使用Executors.newFixedThreadPool()和newSingleThreadExecutor()
3. 短时任务可使用newCachedThreadPool()
4. 定时任务使用newScheduledThreadPool()
第三部分:线程池核心参数
深入理解线程池配置参数
学习目标
掌握corePoolSize, maxPoolSize, keepAliveTime, workQueue等参数的作用
corePoolSize(核心线程数)
线程池中保持活动状态的线程数量
特点: 即使空闲也不会被回收
设置建议: 根据CPU核心数和任务类型设置
maxPoolSize(最大线程数)
线程池允许创建的最大线程数量
特点: 当队列满时创建新线程
设置建议: 根据系统资源和任务负载设置
keepAliveTime(空闲时间)
非核心线程空闲时的存活时间
特点: 超时后线程被回收
设置建议: 根据任务到达频率设置
workQueue(任务队列)
保存等待执行的任务队列
常用队列:
- LinkedBlockingQueue(无界队列)
- ArrayBlockingQueue(有界队列)
- SynchronousQueue(直接传递队列)
- PriorityBlockingQueue(优先级队列)
RejectedExecutionHandler(拒绝策略)
当任务无法执行时的处理策略
内置策略:
- AbortPolicy(默认):抛出RejectedExecutionException
- CallerRunsPolicy:由调用线程执行任务
- DiscardPolicy:直接丢弃任务
- DiscardOldestPolicy:丢弃队列最前面的任务
ThreadFactory(线程工厂)
创建新线程的工厂类
作用:
- 自定义线程名称
- 设置线程优先级
- 设置线程守护状态
- 设置UncaughtExceptionHandler
核心线程 (corePoolSize)
长期存活的线程,处理常规任务
任务队列 (workQueue)
保存等待执行的任务
非核心线程 (maxPoolSize - corePoolSize)
临时线程,处理突发任务
拒绝策略 (RejectedExecutionHandler)
当队列和线程都满时处理新任务
第四部分:任务提交与执行
掌握任务提交方式与执行结果获取
学习目标
理解execute()和submit()的区别,掌握Future的使用
execute()方法
提交不需要返回值的任务
特点: 无返回值,无法获取任务执行结果
submit()方法
提交需要返回值的任务
特点: 返回Future对象,可获取任务结果
invokeAll()方法
批量提交任务并等待所有任务完成
invokeAny()方法
提交多个任务,返回任意一个完成的任务结果
第五部分:线程池生命周期
掌握线程池的启动、关闭与状态转换
学习目标
理解线程池状态转换,掌握shutdown()和shutdownNow()的区别
RUNNING
运行状态,接受新任务并处理队列任务
SHUTDOWN
关闭状态,不接受新任务但处理队列任务
STOP
停止状态,不接受新任务,不处理队列任务,中断正在执行的任务
TIDYING
整理状态,所有任务终止,workerCount=0
TERMINATED
终止状态,terminated()方法执行完成
shutdown()方法
平滑关闭线程池
特点: 等待已提交任务完成,不接受新任务
shutdownNow()方法
立即关闭线程池
特点: 尝试中断正在执行的任务,返回未执行任务列表
关闭建议
1. 优先使用shutdown() + awaitTermination()组合
2. 超时后使用shutdownNow()强制关闭
3. 处理中断异常时恢复中断状态
4. 使用isShutdown()和isTerminated()检查状态
第六部分:常见错误与陷阱
避免线程池使用中的常见问题
1. 使用无界队列
导致内存溢出
正确做法:使用有界队列并设置拒绝策略
2. 忽略拒绝策略
任务被静默丢弃
正确做法:设置合适的拒绝策略
3. 忘记关闭线程池
导致线程泄漏,JVM无法退出
正确做法:使用try-finally或try-with-resources关闭线程池
4. 错误配置线程数
CPU密集型任务配置过多线程
正确做法:CPU密集型任务线程数 ≈ CPU核心数
5. 未处理任务异常
任务异常导致线程终止
正确做法:在任务内部捕获异常或设置UncaughtExceptionHandler
6. 阻塞任务过多
IO密集型任务阻塞线程
正确做法:增加线程数或使用异步非阻塞IO
第七部分:线程池最佳实践
高效、安全地使用线程池
线程池优点
- 资源复用,减少创建销毁开销
- 控制并发数量,避免资源耗尽
- 统一管理线程,提高稳定性
- 提供任务队列,支持任务调度
线程池缺点
- 配置复杂,参数调优困难
- 不当使用可能导致内存溢出
- 任务异常可能导致线程终止
- 死锁问题难以排查
实践1:合理配置参数
- CPU密集型:线程数 ≈ CPU核心数
- IO密集型:线程数 ≈ CPU核心数 * (1 + 等待时间/计算时间)
- 混合型:根据任务比例调整
实践2:使用有界队列
- 避免无界队列导致内存溢出
- 设置合理的队列容量
- 配合拒绝策略处理队列满的情况
实践3:监控线程池状态
- 监控线程池状态和队列大小
- 使用ThreadPoolExecutor的扩展方法
- 集成监控系统
高级技巧
1. 使用自定义ThreadFactory设置线程名称
2. 重写beforeExecute和afterExecute添加日志
3. 使用CompletableFuture组合异步任务
4. 使用ForkJoinPool处理分治任务
5. 使用Spring的@Async简化异步调用
第八部分:练习与测验
通过练习巩固线程池知识
动手练习
- 实现一个自定义拒绝策略
- 创建监控线程池状态的工具类
- 使用线程池实现Web服务器请求处理
- 实现一个可动态调整大小的线程池
线程池知识测验
学习资源推荐
1. 《Java并发编程实战》第6章、第8章
2. Oracle官方文档:java.util.concurrent
3. ThreadPoolExecutor源码分析
4. Java并发编程之美:线程池原理