单例模式(Singleton Pattern)
单例模式,即限制类只能实例化一个实例的设计模式
a software design pattern that restricts the instantiation of a class to a singular instance.
通常而言单例模式满足以下特征:
- 确保只有一个实例Ensure they only have one instance
- 提供获取该实例的途径Provide easy access to that instance
- 控制实例化的过程(比如私有化构造器)Control their instantiation (for example, hiding the constructors of a class)
映射至现实世界,假设一个办公室的所有手机都有连接WiFi的需求,那么并不需要为每部手机提供一个WiFi,而是一个WiFi供所有手机使用,那么这个WiFi就是单例模式。显而易见,这样的设计大大节省了资源!
追溯至Spring的代码世界,IOC容器就是单例模式的一种著名的具体实现,@Controller、@Service、@Component、@Configuration、@Bean等方式
注入至IOC容器的Bean,通常都由IOC进行实例化管理,并通过@Autowired、@Resource、构造器等方式
方便的拿取并使用。
但是除了IOC实现单例模式之外,还有如下两种常见实现:
- 1️⃣双重校验锁(Double-Checked Locking,DCL)
- 2️⃣CAS(Compare-And-Swap)
先简单谈及两者的优缺点
1️⃣严格意义的单例,多线程场景的使用下存在一定阻塞情况。2️⃣非严格意义的单例,存在重复实例化的可能,避免了锁的开销,适用于高并发场景
既然有了IOC的单例实现,为什么我们仍然需要自己来实现单例呢?比如在写一些工具类的时候,该工具类的使用频率较低,注入IOC会使得该对象没有被使用的时候无法被GC回收,从而浪费了memory。或者在某些古老的屎山项目中,你会发现只能通过xml来往IOC注入Bean……在这些特殊场景下,自定义实现单例模式,往往更方便更自主可控。
/**
* 适用于使用频率较低的工具类
*/
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;
}
}
/**
* 高并发场景(若条件满足,请直接选择IOC的单例)
*/
public class Singleton {
private static final AtomicReference<Singleton> instance = new AtomicReference<>();
private Singleton() {
// 初始化逻辑
}
public static Singleton getInstance() {
for (;;) {
Singleton current = instance.get();
if (current != null) {
return current;
}
current = new Singleton();
if (instance.compareAndSet(null, current)) {
return current;
}
}
}
}