上午 3h 线程池基础概念 + Executors 工具类
1 线程池是什么(0.5h 精讲)
原生问题(不用线程池的弊端)
以前写法:每来一个任务就 new Thread ()
- 线程创建、销毁开销极大,浪费 CPU、内存资源
- 任务多了会无限创建线程,系统卡顿、OOM 内存溢出
- 线程杂乱无章,无法统一管理并发数量
线程池核心定义
提前初始化一批线程 放在「池子」里,复用已有线程 ,不用频繁新建 / 销毁;任务来了直接分配池内空闲线程执行,执行完线程不销毁、回归池里待命。
通俗比喻
- 原生 new Thread:来一个客人招一个临时工,干完就辞退,成本极高
- 线程池:提前养一批正式员工,来任务就安排员工做,做完员工待命不辞退
2 线程池核心优势(0.3h 必背)
- 降低资源消耗:复用线程,避免频繁创建、销毁的系统开销
- 提高响应速度:任务到达不用新建线程,直接拿空闲线程立刻执行
- 统一管理:控制并发线程数、排队任务、统一关闭、调度管控
- 规避无限创建线程:限制最大并发,防止系统崩溃
3 Executors 工具类 + 三种常用线程池(2.2h 精讲 + 完整代码)
前置知识点
Executors:JDK 官方工具类 ,专门快速创建线程池,不用自己底层构造。核心提交任务方法:execute(Runnable任务)
3.1 newFixedThreadPool (int 线程数) 固定数量线程池
特点
- 池内线程数量固定不变
- 任务多于线程数时,任务进入队列排队
- 适合:并发量固定、服务器稳定业务
完整代码 + 逐行解析
java
运行
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 自定义任务
class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
// 获取当前执行任务的线程名
System.out.println(Thread.currentThread().getName() + " 正在执行:" + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 完成:" + name);
}
}
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 1. 创建固定3个线程的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 2. 提交5个任务
for (int i = 1; i <= 5; i++) {
pool.execute(new MyTask("任务" + i));
}
// 3. 用完关闭线程池
pool.shutdown();
}
}
运行现象
始终只有3 个线程轮流复用执行 5 个任务,多余任务排队等待。
核心结论
线程不会新建更多,固定并发数,可控稳定。
3.2 newSingleThreadExecutor () 单线程池
特点
- 池内永远只有 1 个线程
- 所有任务按顺序排队执行,不会并发
- 适合:需要任务有序执行、不能同时跑的场景
完整代码
java
运行
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadDemo {
public static void main(String[] args) {
// 创建单线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
// 提交4个任务
for (int i = 1; i <= 4; i++) {
pool.execute(new MyTask("有序任务" + i));
}
pool.shutdown();
}
}
运行现象
任务 1 执行完→任务 2→任务 3→任务 4,严格串行有序,无并发争抢。
3.3 newCachedThreadPool () 可缓存线程池
特点
- 无固定线程数,按需创建
- 来了任务有空闲线程就复用,没有就新建
- 空闲线程闲置 60 秒自动回收
- 适合:短期大量瞬时任务,不适合高并发长耗时业务(容易无限建线程)
完整代码
java
运行
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
// 提交6个任务
for (int i = 1; i <= 6; i++) {
pool.execute(new MyTask("缓存任务" + i));
}
pool.shutdown();
}
}
运行现象
瞬间创建多个线程,几乎所有任务同时并发执行。
下午 2.5h 线程池使用细节 + 综合案例
1 线程池使用标准步骤(必背模板)
- 通过
Executors创建线程池ExecutorService - 自定义任务类 implements Runnable,重写 run ()
- 线程池对象调用
execute(任务对象)提交任务 - 所有任务执行完毕,调用
shutdown()关闭线程池
2 线程池执行核心特点
- 线程复用 :线程执行完任务不销毁,回到池里等待下一个任务
- 固定线程池:最大并发 = 设置的线程数,多余任务排队
- 单线程池:天然串行,任务排队有序执行
- 缓存线程池:弹性扩容,空闲线程自动回收
3 综合案例 1:固定线程池观察线程复用
直接用上面 newFixedThreadPool(3) 代码重点观察:
- 始终就那 3 个线程名称反复出现
- 不是每次任务都新建线程 → 完美复用
4 综合案例 2:单线程池有序排队
用 newSingleThreadExecutor() 代码观察:任务严格按提交顺序执行,无抢占、无乱序。
晚上 1.5h 复盘 + 巩固练习
必做练习
- 分别手写三种线程池,提交任务运行,看执行区别
- 对比:普通
new Thread()每次都是新线程;线程池复用旧线程 - 练习
shutdown()关闭,不关闭程序可能一直不退出
今日核心总结表格
表格
| 线程池类型 | 线程数量 | 执行特点 | 适用场景 |
|---|---|---|---|
| FixedThreadPool | 固定 n 个 | 固定并发,任务排队 | 常规业务、控制并发量 |
| SingleThreadExecutor | 固定 1 个 | 串行有序执行 | 任务必须按顺序执行 |
| CachedThreadPool | 弹性不固定 | 按需新建、空闲回收 | 短期大量瞬时小任务 |
原计划缺失 超重要知识点(必须补充,Day28 必学)
1. 为什么阿里开发规范禁止用 Executors 创建线程池
- Fixed/Single 任务队列无界,任务太多内存溢出 OOM
- Cached 无最大线程限制,任务暴增会无限创建线程拖垮服务器
- 实际开发要用:ThreadPoolExecutor 手动创建底层线程池(后续学)
2. shutdown () 注意事项
shutdown():不再接受新任务,已提交任务执行完再关闭- 不调用 shutdown:线程池核心线程一直存活,程序不会自动结束
3. 线程池核心底层七大参数(先认知,后续深挖)
核心线程数、最大线程数、空闲存活时间、阻塞队列、拒绝策略等,是线程池本质,Executors 只是封装工具。
4. 任务提交两种方式
execute(Runnable):无返回值submit(Callable):有返回值,可以获取线程执行结果(衔接 Day21 Callable)
Day28 验收标准(强化版)
- 能口述线程池作用、四大优势,说出和原生 new Thread 的区别
- 能手写三种线程池创建代码、提交任务、关闭线程池
- 能说出三种线程池各自特点、运行现象、适用场景
- 理解线程复用原理,知道线程执行完不销毁
- 知道 Executors 弊端,了解实际开发不推荐直接使用
| 线程池 | 执行顺序 | 线程数量 | 抢占式? |
|---|---|---|---|
| Fixed 固定线程池 | 乱序 | 固定 | ✅ 抢占式 |
| Cached 可缓存线程池 | 最乱、完全无序 | 自动增加 | ✅ 抢占式(最强) |
| Single 单线程池 | 绝对有序 | 1 个 | ❌ 不抢占,排队执行 |