一.单例模式的定义
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供该实例的全局访问点。
1.1.核心目标
- 唯一实例:限制类的实例化次数仅一次。
- 全局访问:提供统一的访问入口(通常是静态方法)。
1.2.实现步骤
- 私有化构造函数:禁止外部通过
new
创建实例。 - 静态私有成员:保存类的唯一实例。
- 静态公有方法:提供全局访问入口,如
getInstance()
.1.3.优点
- 资源共享:如数据库连接池、线程池、配置文件管理器等全局资源。
- 状态一致性:避免多个实例导致状态冲突(如计数器)。
- 性能优化:减少重复创建昂贵对象的开销
二.单例模式的实现方式
2.1.饿汉式实现
- 特点:类加载时立即创建实例(线程安全)。
- 优点:简单高效,无同步开销。
- 缺点:可能造成资源浪费(即使未使用也会创建实例)。
/**
* 饿汉式单例
*/
public class EagerSingleton {
// 创建一个静态的实例,静态常量,在类加载的时候创建实例
private static final EagerSingleton instance = new EagerSingleton();
// 私有化构造方法,使的用者无法通过new关键字创建对象
private EagerSingleton() {
}
// 提供获取实例的方法
public static EagerSingleton getInstance() {
return instance;
}
}
public class demo {
public static void main(String[] args) {
EagerSingleton instance = EagerSingleton.getInstance();
EagerSingleton instance2 = EagerSingleton.getInstance();
System.out.println(instance == instance2); // true
}
}
2.2.懒汉式实现
- 特点:延迟实例化(首次调用
getInstance()
时创建)。 - 优点:首次创建后不再同步,兼顾性能与安全。
- 注意:必须使用
volatile
关键字(避免JVM指令重排序导致未初始化完全的错误实例)。
/**
* 懒汉式单例普通实现
*/
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
/**
* 在多线程环境下,多个线程可能同时进入 if (instance == null) 条件块,
* 并且每个线程都可能执行 new LazySingleton() 语句,从而导致创建多个实例
* @return
*/
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton(); // 这里可能会有并发问题
}
return instance;
}
}
/**
* 懒汉式单例双重检查锁定实现
*/
public class LazySingleton {
// volatile 关键字是为了避免指令重排
private static volatile LazySingleton instance;
private LazySingleton() {
}
/**
* 在多线程环境下,多个线程可能同时进入 if (instance == null) 条件块,
* 并且每个线程都可能执行 new LazySingleton() 语句,从而导致创建多个实例
* 所以需要使用 synchronized 关键字修饰 getInstance() 方法,
* @return
*/
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class){
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
2.3.静态内部类实现
- 特点:利用类加载机制保证线程安全,延迟加载。
- 优势:线程安全(JVM保证类加载过程的互斥性)。无同步开销,高效延迟加载。
/**
* 静态内部类实现单例模式
*/
public class HolderSingleton {
private HolderSingleton() {
}
/**
* JVM 确保一个类只会被初始化一次,即使多个线程同时尝试加载该类。
* 当 HolderSingleton 类被加载时,其内部的 Holder 类并不会立即被加载
*/
private static class Holder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
/**
* 延迟加载
* 只有当调用 getInstance() 方法时,才会触发 Holder.INSTANCE 的访问
* 此时 JVM 才会加载并初始化 Holder 类,并创建 HolderSingleton 实例
* @return
*/
public static HolderSingleton getInstance() {
return Holder.INSTANCE;
}
}
2.4.枚举实现
- 特点:天然防反射/序列化破坏,简洁安全。
- 调用方式:
Singleton.INSTANCE.doSomething()
- 优势:绝对单例(JVM保障)。自动处理序列化和反射攻击。
/**
* 枚举单例模式
*/
public enum EnumSingleton {
//
/**
* 枚举实例由JVM在类加载时静态初始化,保证线程安全
* Java规范禁止通过反射创建枚举实例,避免传统单例被反射攻击的问题
* 枚举的序列化机制仅保存枚举名称,反序列化时通过valueOf还原原实例,防止生成新对象
*/
INSTANCE; // 唯一实例
/**
*实例方法
*/
public void whateverMethod() {
System.out.println("whateverMethod");
}
}
public static void main(String[] args) {
//调用
EnumSingleton.INSTANCE.whateverMethod();
}
三.应用场景
3.1.初始化开销
- 轻量级对象(CPU耗时<50ms,内存<1MB):
- 适用场景:配置管理器、工具类(如字符串处理器)16
- 饿汉式:实例在类加载时创建,启动速度快
- 重量级对象(数据库连接、大型缓存):
- 适用场景:数据库连接池、日志系统
- 静态内部类/DCL:延迟加载,避免启动阻塞
3.2.线程安全
- 高并发场景:
- 双重检查锁(DCL):通过
volatile
+同步块确保安全(如支付网关)36 - 枚举:JVM保障线程安全(如金融交易引擎)512
- 双重检查锁(DCL):通过
- 低并发场景:
- 静态内部类:无锁延迟加载(如配置文件读取器)
3.3.防破坏需求
- 枚举:Java规范禁止反射创建枚举实例
3.4.场景化推荐
场景 | 推荐模式 | 原因 |
---|---|---|
全局配置管理器 | 饿汉式 | 配置轻量且启动必用 |
数据库连接池 | 静态内部类 | 延迟加载避免启动卡顿,无锁线程安全 |
金融交易核心 | 枚举 | 防反射攻击,强一致性要求 |
高频工具类 | 饿汉式 | 无状态对象,快速访问 |
第三方服务代理 | 双重检查锁(DCL) | 按需加载+高并发安全(如支付网关) |
💡 如果本文对你有帮助,点击右上角【订阅专栏】或左上角关注我
🔔 完整的23中设计模式干货,第一时间推送给你!