单例模式
单例模式
其目的是再全局范围内同一个类只能有一个(或有限数量)实例,并提供一个全局的访问点。
优点
对于占用资源比较大的对象,例如数据源对象,使用单例可以减少内存开销,避免资源多重占用, 严格控制对象的访问
缺点
没有接口,扩展困难(只能修改代码)
单例的基本准则
- 私有化构造器
- 提供获取实例的方法
单例模式的分类
- 懒汉模式
- 饿汉模式
- 双重检查模式
- 注册式单例
- ThreadLocal
- 内部类实现单例模式
饿汉式单例
特点
- 在类加载时就会创建实例,无论该实例是否真正用到。
- 线程安全(static,由JVM保证线程安全)
/**
* 饿汉式单例模式
*/
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
懒汉式单例
特点
- 在真正需要使用对象时才会创建对象实例。
线程不安全
(多线程下可能多个线程同时进入null判断)
/**
* 懒汉式单例
*/
public class LazySingleton {
private static LazySingleton INSTANCE = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (INSTANCE == null) { //unsafe 多线程
INSTANCE = new LazySingleton();
}
return INSTANCE;
}
}
线程安全的懒汉式
- 在方法上直接加锁
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
public SynchronizedSingleton() {
}
//直接在方法上加锁,存在一定的性能问题
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
- 双重检查+锁
/**
* 双重检查
*/
public class DoubleCheckSingleton {
//volatile 解决指令重排问题
private volatile static DoubleCheckSingleton instance = null;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) { //第二次检查实例
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
内部类模式
特性
- 不使用锁
- 利用了类加载的特性(在使用到的时候才加载且JVM保证了一个类只加载依次即线程安全)
public class InnerClassSingleton {
private InnerClassSingleton() {
if (InstanceHolder.instance != null) { //避免反射攻击
throw new IllegalStateException("不允许反射创建实例");
}
}
public static InnerClassSingleton getInstance() {
return InstanceHolder.instance;
}
private static class InstanceHolder {
private static InnerClassSingleton instance = new InnerClassSingleton();
}
}
破坏单例的方式
- 反射
- 反序列化
解决
反射
以上所有的单例都可能被反射攻击(直接获取类的构造器来实例化对象)d
public static void main(String[] args) {
try {
Constructor<InnerClassSingleton> cons = InnerClassSingleton.class.getDeclaredConstructor(null);
InnerClassSingleton instance = cons.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
在构造方法中添加非空判断
//inner class
private InnerClassSingleton() {
if (InstanceHolder.instance != null) { //避免反射攻击
throw new IllegalStateException("不允许反射创建实例");
}
}
//懒汉式
private DoubleCheckSingleton() {
if (instance != null) {
throw new IllegalStateException("不允许反射构建实例");
}
}
反序列化
public class SeriableSingleton implements Serializable {
private SeriableSingleton() {
}
private static SeriableSingleton instance = new SeriableSingleton();
private SeriableSingleton getInstance() {
return instance;
}
//解决反序列化
//在反序列化时仍然会创建一次对象,但是在jvm中就将反序列化出来的对象使用此方法中的对象将其替换掉了
private Object readResolve() {
return instance;
}
}
ObjectInputStream
注册式单例
- 枚举
- 枚举不能被反射创建
- 容器
ThreadLoacl
ThreadLoacl 以当前线程为key, value为用户给定的值, 所以当前线程取值的时候只会取到当前线程设置的值。属于容器式单例
/**
* 每一个线程一个实例
*/
public class ThreadLocalSingleton {
private ThreadLocalSingleton() {
}
private static final ThreadLocal<ThreadLocalSingleton> instance = new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
public static ThreadLocalSingleton getInstance() {
return instance.get();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义