线程池 · 设计模式(单例 · 工厂) · 重点总结 一、线程池 1.1 为什么需要线程池
问题
说明
频繁创建/销毁线程
线程属于系统宝贵资源,每次 new Thread() 都需要向 OS 申请资源,用完即销毁,效率低
线程池的作用
预先创建好一批线程,任务来了直接从池中取,用完归还,实现线程复用 ,提升效率
1.2 核心接口与类
类/接口
说明
Executor
线程池顶层接口,定义 execute(Runnable) 方法
ExecutorService
常用子接口,扩展了更多功能(submit、shutdown 等)
ThreadPoolExecutor
推荐使用的线程池实现类 ,7 参数构造器,灵活可控
Executors
工具类,提供快速创建线程池的静态方法(不推荐生产使用 ,有 OOM 风险)
1.3 ThreadPoolExecutor 七大参数(⭐ 重点)
参数位置
参数名
类型
说明
1
corePoolSize
int
核心线程数 ,一直保持存活(即使空闲)
2
maximumPoolSize
int
最大线程数 ,临时线程数 = 最大 - 核心
3
keepAliveTime
long
临时线程存活时间 ,超时没有任务则销毁
4
unit
TimeUnit
存活时间单位(TimeUnit.SECONDS 等)
5
workQueue
BlockingQueue
任务阻塞队列 ,来不及处理的任务在此等待
6
threadFactory
ThreadFactory
创建线程的工厂,一般用 Executors.defaultThreadFactory()
7
handler
RejectedExecutionHandler
拒绝策略 ,满载时对新任务的处理方式
临时线程数 = maximumPoolSize - corePoolSize
1.4 四种拒绝策略
拒绝策略类
说明
AbortPolicy(默认)
直接抛出 RejectedExecutionException
CallerRunsPolicy
由提交任务的线程 自己执行该任务
DiscardPolicy
直接丢弃 新任务,不报错
DiscardOldestPolicy
丢弃队列中最老的 任务,把新任务加进来
1.5 临时线程创建 & 任务拒绝时机(⭐ 常考)
时机
条件
临时线程创建
核心线程全忙 AND 任务队列已满 AND 还未达到最大线程数
开始拒绝任务
核心线程全忙 AND 临时线程也全忙 AND 任务队列也满
1 2 3 4 5 6 7 8 任务提交流程: 新任务 → 核心线程有空闲?→ 有:直接执行 ↓ 否 任务队列未满?→ 是:放入队列等待 ↓ 否 未达到最大线程?→ 是:创建临时线程执行 ↓ 否 执行拒绝策略
1.6 核心线程数配置建议
任务类型
建议核心线程数
说明
IO 密集型 (文件读写、网络请求)
CPU 核数 × 2
线程常在等待 IO,可多配
计算密集型 (大量运算)
CPU 核数 + 1
线程一直在计算,太多反而切换开销大
1.7 执行任务的两种方法
方法
适用任务
返回值
pool.execute(Runnable task)
Runnable 任务
无返回值
pool.submit(Callable task)
Callable 任务
返回 Future,可通过 future.get() 获取结果
1.8 完整代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ExecutorService pool = new ThreadPoolExecutor ( 3 , 5 , 8 , TimeUnit.SECONDS, new ArrayBlockingQueue <>(4 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .CallerRunsPolicy() ); for (int i = 1 ; i <= 10 ; i++) { final int taskId = i; pool.execute(() -> { String name = Thread.currentThread().getName(); System.out.println(name + " 执行任务:" + taskId); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } }); } Future<Integer> future = pool.submit(() -> { Thread.sleep(500 ); return 100 * 2 ; }); System.out.println("任务结果:" + future.get()); pool.shutdown();
1.9 Executors 工具类(了解,不推荐生产使用)
方法
说明
风险
newFixedThreadPool(n)
固定线程数的线程池
任务队列无界(LinkedBlockingQueue),可能 OOM
newCachedThreadPool()
线程数可无限增长的线程池
最大线程数为 Integer.MAX_VALUE,可能 OOM
newSingleThreadExecutor()
只有一个线程的线程池
同 newFixedThreadPool,任务队列无界
newScheduledThreadPool(n)
支持定时/周期执行的线程池
同 newCachedThreadPool
⚠️ 阿里巴巴 Java 开发手册强制规范 :禁止使用 Executors 创建线程池 ,应使用 ThreadPoolExecutor 明确指定参数,避免无界队列/无限线程数导致 OOM。
二、设计模式 2.1 概述
项目
说明
定义
被反复使用、经过分类编目的代码设计经验总结,用于可重用代码、保证可靠性
来源
1995 年 GoF(四人组)出版《设计模式:可复用面向对象软件的基础》,共 23 种
2.2 三大分类
类型
包含模式(常考)
核心目的
创建型 (5种)
工厂方法、抽象工厂、单例 、建造者、原型
如何创建对象
结构型 (7种)
适配器、装饰器、代理 、外观、桥接、组合、享元
对功能进行增强/组合
行为型 (11种)
策略 、模板方法 、观察者、迭代子、责任链、命令等
描述类/对象如何交互
三、单例设计模式 3.1 核心概念
项目
说明
目的
保证一个类在整个程序生命周期中只能创建一个对象
三步骤
① 私有化构造方法;② 内部创建静态唯一对象;③ 提供静态方法返回该对象
应用场景
配置类、连接池、工具类、日志对象等需要全局唯一的场景
3.2 饿汉式 vs 懒汉式对比(⭐ 常考)
对比项
饿汉式
懒汉式(双重检测)
对象创建时机
类加载时 立即创建
第一次调用 getInstance() 时创建
线程安全
✅ 天然安全(static 初始化由 JVM 保证)
需要 synchronized + volatile 保证
内存占用
程序启动即占用内存
用时才占用,节省内存
代码复杂度
简单
较复杂(双重 if + 锁 + volatile)
推荐场景
对象较小、必定使用
对象较大、可能不使用
3.3 饿汉式代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class A { private A () { if (a != null ) { throw new RuntimeException ("不能创建多个对象!" ); } } private static final A a = new A (); public static A getInstance () { return a; } } public class TestA { public static void main (String[] args) { Runnable r = ()->{ String name = Thread.currentThread().getName(); for (int i = 0 ; i < 3 ; i++) { System.out.println(name+"获取的A=" +A.getInstance()); } }; for (int i = 0 ; i < 10 ; i++) { new Thread (r).start(); } } }
3.4 懒汉式(双重检测锁,线程安全版) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class B { private B () { if (a != null ) { throw new RuntimeException ("不能创建多个对象!" ); } } private static volatile B a; public static B getInstance () { if (a == null ) { synchronized (B.class) { if (a == null ) { a = new B (); } } } return a; } } public class TestB { public static void main (String[] args) { Runnable r = ()->{ String name = Thread.currentThread().getName(); for (int i = 0 ; i < 3 ; i++) { System.out.println(name+"获取的B=" + B.getInstance()); } }; for (int i = 0 ; i < 10 ; i++) { new Thread (r).start(); } } }
为什么需要双重 if 检测?
第一个 if:已创建则直接返回,避免每次调用都加锁(性能优化)
synchronized:保证只有一个线程进入创建逻辑
第二个 if:防止多个线程都通过第一个 if 后,轮流进入锁内重复 new 对象
为什么需要 volatile? new B() 底层分三步:① 申请内存空间 → ② 执行构造方法初始化 → ③ 将地址赋给变量 a。JVM 可能将步骤②③重排为③②(先赋值再初始化),此时其他线程看到 a != null 但对象实际未初始化完成,会返回一个”半初始化”的对象。volatile 禁止这种指令重排。
四、工厂设计模式 4.1 核心概念
项目
说明
目的
提供一个统一的方法来创建对象,屏蔽创建细节
好处1
封装创建细节,可在创建时进行数据注入和加工
好处2(核心)
解耦 :调用者只需要知道接口/抽象类,不需要知道具体实现类
4.2 工厂模式结构
角色
说明
抽象产品
定义产品公共接口/抽象类
具体产品
实现/继承抽象产品的具体类
工厂类
提供静态方法,根据参数创建并返回具体产品对象
4.3 工厂模式完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public abstract class Car { private String brand; private String color; private int price; public abstract void run () ; } public class BmwCar extends Car { @Override public void run () { System.out.println(getBrand() + "::" + getColor() + "::" + getPrice() + "...宝马疯狂跑..." ); } } public class AudiCar extends Car { @Override public void run () { System.out.println(getBrand() + "::" + getColor() + "::" + getPrice() + "...奥迪冒烟跑..." ); } } public class CarFactory { public static Car createCar (String type) { switch (type) { case "bmw" : Car bmw = new BmwCar (); bmw.setBrand("BMW750Li" ); bmw.setColor("宝强绿" ); bmw.setPrice(999999 ); return bmw; case "audi" : Car audi = new AudiCar (); audi.setBrand("奥迪A8L" ); audi.setColor("骚红" ); audi.setPrice(888888 ); return audi; default : return null ; } } } public class Demo { public static void main (String[] args) { Car car1 = CarFactory.createCar("bmw" ); car1.run(); Car car2 = CarFactory.createCar("audi" ); car2.run(); } }
五、综合对比速查 线程池关键参数速记 1 2 3 核心线程 → 任务队列 → 临时线程 → 拒绝策略 3 个 满了 创建 CallerRunsPolicy (一直存活) 等待 (空闲销毁) (四种选一)
单例模式三步口诀 1 2 3 ① 私有构造(外部不能 new) ② 私有静态变量(内部唯一对象) ③ 公开静态方法(对外获取唯一对象)
两种单例对比速记
特征
饿汉式
懒汉式
何时创建
类加载时
第一次使用时
关键字
static final
volatile + synchronized
线程安全
天然安全
双重检测保证
工厂模式 vs 直接 new 1 2 3 4 5 6 7 8 9 直接 new: 调用方需要知道 BmwCar、AudiCar 类名 调用方需要自己注入数据 调用方与具体实现类强耦合 工厂模式: 调用方只知道抽象类 Car + 工厂类 CarFactory 工厂负责数据注入 调用方与具体实现类解耦(换实现只改工厂,调用方不变)