概念
一个类在整个应用运行期间,只能有一个实例,而且这个实例对外提供一个全局访问点
应用场景
对于全局只会使用一个实例的类,使用单例模式减少频繁创建销毁的性能开销

基本结构
结构图

instance:私有静态实例变量
Singleton() :私有构造方法,防止外部实例化
getInstance(): 对外暴露的获取实例的静态方法
设计要求
1)私有化构造器:禁止外部直接 new,确保外部无法创建对象
2)唯一实例的静态变量:通常写成 private static Singleton instance;,程序启动或首次访问时再创建
3)全局访问点:提供一个 public static getInstance()方法,外部就通过这个方法拿到唯一实例
4)线程安全:在多线程场景下,保证并发时也只有一份
代码实现
饿汉式
在类加载阶段就完成实例化,依赖 JVM 的类加载机制来确保线程安全
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return INSTANCE;
}
}
懒汉式
懒汉式在第一次调用 getInstance() 时才创建实例,通过对该方法加锁来保证线程安全
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查锁定
结合了懒汉式的延迟加载和饿汉式的高性能。首次创建时加锁,后续访问则跳过同步块,从而减少锁开销
public class Singleton {
private static volatile Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
利用 JVM 在加载外部类时并不立即加载内部类的特性,将实例的创建延迟到真正访问内部类时。既能延迟加载,又能借助类加载的线程安全特性
public class Singleton {
private Singleton() { }
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
枚举式
利用 Java 枚举类型的特性,枚举值在类加载时就创建,JVM 保证枚举实例的线程安全和唯一性。同时,枚举对序列化和反射攻击具有天然防护能力
public enum Singleton {
INSTANCE;
}
优缺点
优点
1)全局唯一,资源可控
保证某个类在系统中始终只有一个实例,对一些需要全局共享资源的场景既节省资源又避免冲突
2)统一访问点
不用每次都去 new,代码更简洁
缺点
1)引发隐藏的状态问题
由于单例在多个地方被共享使用,容易导致不小心在一个地方修改了它的状态,影响到其他地方的行为
2)生命周期不可控
单例通常在 JVM 生命周期内都存在,不容易销毁,资源无法及时释放
