设计模式-GOF-创建型-Singleton单例模式

本文深入解析Singleton单例模式,探讨其定义、优缺点、应用场景及多种实现方式,包括饿汉式、懒汉式、双重校验锁、静态内部类与枚举等,对比不同实现的线程安全性和Lazy加载特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式-GOF-创建型-Singleton单例模式

定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
主要解决: 一个全局使用的类频繁地创建与销毁
优点:

  • 在内存中只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
  • 避免对资源的多重占用

特点:

  • 单例类只有一个实例对象
  • 该单例对象必须由单例类自行创建
  • 单例类对外提供一个访问该单例的全局访问点

使用场景

  • 创建一个对象需要消耗的资源过多,比如I/O与数据库的连接
  • 为控制实例数量,节省系统资源
  • 全局数据共享

实现

  • 私有化构造器,以防外部类来通过new生成实例
  • 定义一个静态私有实例,并对外提供一个静态方法用于创建或获取静态私有实例

几种不同的实现方式

实现方式是否线程安全是否懒加载是否防止反射构建是否使用类加载机制
饿汉式
双重校验锁
静态内部类
枚举

枚举与类加载机制是否有关还没理解透

懒汉式,线程不安全

是否Lazy初始化:是
描述:实现最简单但不支持多线程。

public class SingleObject {
 private static SingleObject instance;
 private SingleObject() {}
 public static SingleObject getInstance() {
  if(instance==null) {
   instance = new SingleObject();
  }
  return instance;
 }
}

懒汉式,线程安全

是否Lazy初始化:是
描述:这种方式具备很好的Lazy loading,能保证多线程安全,但效率低,99%的情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费
缺点:加锁影响效率

public class SingleObject {
 private static SingleObject instance;
 private SingleObject() {}
 public static synchronized SingleObject getInstance() {
  if(instance==null) {
   instance = new SingleObject();
  }
  return instance;
 }
}

饿汉式(类加载时创建)

是否Lazy初始化:否
是否多线程安全:是
描述:比较常用,但容易产生垃圾对象
优点:没有锁,效率高
缺点:类加载时就初始化,浪费内存
它基于classloader机制避免了多线程同步问题,但没有达到lazy loading的效果

public class SingleObject {
 private static SingleObject instance = new SingleObject();
 private SingleObject() {}
 public static SingleObject getInstance() {
  return instance;
 }
}

双检锁/双重校验锁(DCL,即double-checked locking)

JDK版本:JDK1.5起
是否Lazy初始化:是
是否多线程安全:是?
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。这种方法还有漏洞不能保证绝对的线程安全

public class SingleObject {
 private static SingleObject instance;
 private SingleObject() {}
 public static SingleObject getInstance() {
  if(instance==null) {
   synchronized (SingleObject.class) {
    if(instance==null) {
     instance = new SingleObject();
    }
   }
  }
  return instance;
 }
}

多线程不绝对安全的原因:
JVM会进行指令重排
创建对象有四步:

  1. 分配对象的内存空间
  2. 初始化对象
  3. 执行构造器初始化
  4. 将instance指向刚分配的内存地址(刚创建的对象)
    经过优化顺序可能发生改变,可能导致A线程对象创建还没有完成,但B线程判断instance时instance已经不为null并且返回了一个没有初始化完成的instance对象
改进,添加volatile

volatile 防止JVM进行指令重排

public class SingleObject {
 private volatile static SingleObject instance;
 private SingleObject() {}
 public static SingleObject getInstance() {
  if(instance==null) {
   synchronized (SingleObject.class) {
    if(instance==null) {
     instance = new SingleObject();
    }
   }
  }
  return instance;
 }
}

登记式/静态内部类

是否Lazy初始化:是
是否多线程安全:是
描述:对静态域使用延迟初始化。基于classloader类加载机制实现,在通过显式调用getInstance()时才会加载内部类,从而到达Lazy loading的目的。
注意:内部静态类无法从外部访问

public class SingleObject {
 private static class LazyHolder{
  private static final SingleObject INSTANCE = new SingleObject();
 }
 private SingleObject() {}
 public static SingleObject getInstance() {
  return LazyHolder.INSTANCE;
 }
}

以上方法均可用反射打破单例
方法:

//获得构造器
Constructor con = SingleObject.class.getDeclaredConstructor();
//设置为可访问
con.setAccessible(true);
//构造两个不同的对象
SingleObject single1 = (SingleObject)con.newInstance();
SingleObject single2 = (SingleObject)con.newInstance();
//验证是否是不同对象
System.out.println(single1.equals(single2));

代码可以简单归纳为三个步骤:
第一步,获得单例类的构造器。
第二步,把构造器设置为可访问。
第三步,使用newInstance方法构造对象。
最后为了确认这两个对象是否真的是不同的对象,我们使用equals方法进行比较。毫无疑问,比较结果是false。

枚举

JDK版本:JDK1.5起
是否Lazy初始化:否
比较神奇简洁
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

不能通过 reflection attack 来调用私有构造方法。

public enum SingleObject {
 INSTANCE;
}

具体原理还需研究
备注:

  • volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值
  • 使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。
  • 对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。
private Object readResolve() throws ObjectStreamException{
  return instance;
}

最后
经验之谈(这是我copy来的,具体还是看个人需求吧):一般情况下,不建议使用懒汉式线程不安全懒汉式线程安全,建议使用饿汉式。只有在要明确实现 lazy loading 效果时,才会使用登记式/静态内部类。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁/双重校验锁方式。

参考链接

https://mp.weixin.qq.com/s/2UYXNzgTCEZdEfuGIbcczA

内容概要:该论文探讨了一种基于粒子群优化(PSO)的STAR-RIS辅助NOMA无线通信网络优化方法。STAR-RIS作为一种新型可重构智能表面,能同时反射和传输信号,与传统仅能反射的RIS不同。结合NOMA技术,STAR-RIS可以提升覆盖范围、用户容量和频谱效率。针对STAR-RIS元素众多导致获取完整信道状态信息(CSI)开销大的问题,作者提出一种在不依赖完整CSI的情况下,联合优化功率分配、基站波束成形以及STAR-RIS的传输和反射波束成形向量的方法,以最大化总可实现速率并确保每个用户的最低速率要求。仿真结果显示,该方案优于STAR-RIS辅助的OMA系统。 适合人群:具备一定无线通信理论基础、对智能反射面技术和非正交多址接入技术感兴趣的科研人员和工程师。 使用场景及目标:①适用于希望深入了解STAR-RIS与NOMA结合的研究者;②为解决无线通信中频谱资源紧张、提高系统性能提供新的思路和技术手段;③帮助理解PSO算法在无线通信优化问题中的应用。 其他说明:文中提供了详细的Python代码实现,涵盖系统参数设置、信道建模、速率计算、目标函数定义、约束条件设定、主优化函数设计及结果可视化等环节,便于读者理解和复现实验结果。此外,文章还对比了PSO与其他优化算法(如DDPG)的区别,强调了PSO在不需要显式CSI估计方面的优势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值