单例模式
解释:单例模式属于创建型模式,提供了一种创建对象的最佳方式。该类负责创建自己的对象,同样确保只有单个对象被创建。
应用场景:
(1) 资源共享的情况下,避免频繁创建和销毁消耗系统资源,比如说日志模块和读取系统配置。
(2) 控制资源的情况,方便资源之间的相互通信,比如线程池等。
实现方式:
1. 懒汉式(线程不安全)
是否Lazy 初始化:是
是否线程安全:否
实例代码:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2. 懒汉式(线程安全)
是否Lazy初始化:是
是否线程安全:是
缺点:加锁会影响效率
实例代码:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. 饿汉式
是否 Lazy 初始化:否
是否多线程安全:是
缺点:类加载后就初始化,浪费内存。
实例代码:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4. 双检锁/双重校验锁(DCL,即 double-checked locking)
是否 Lazy 初始化:是
是否多线程安全:是
优点:该方式采用双锁机制,线程安全并且保证高性能。
示例代码:
public class Singleton {
//volatile 禁止代码重排序,避免多线程调用时返回空对象。
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5. 登记式/静态内部类
是否 Lazy 初始化:是
是否多线程安全:是
解释:使用静态内部类,外部类被加载后,instance 不一定被初始化。只有显式调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。
实例代码:
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE =
new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6. 枚举
是否 Lazy 初始化:否
是否多线程安全:是
解释:自动支持序列化机制,防止多次实例化。序列化会生成新的对象,破坏单例模式。要想防止多次实例化,Singleton类要实现Serializable接口,并且重写readResolve方法,返回唯一对象singleton。而枚举类底层已经将这些封装好了,所以枚举类能够防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
实现方式总结:
补充:
破坏单例模式的两种办法:
- 反射
- 序列化
解决办法:
- 反射:无参构造方法增加判断singleton是否为空
- 序列化:Singleton类要实现Serializable接口,并且重写readResolve方法,返回唯一对象singleton
解决问题永远是我最大的快乐,当然,我得首先面对自己,不蒙住自己的眼睛,勇敢的去看到问题,不逃避。
参考文章:
[1] 序列化对单例的破坏
[2] 单例模式
[3] 《Spring 5核心原理与30个类手写实战》 谭勇德