1. 饿汉式
// 饿汉式
public class Singleton {
private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
2. 懒汉式
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
3. 线程安全懒汉式-双重检查
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized(Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
4. 双重检查的不足之处
以下三种情况会破坏双重检查的单例模式:指令重排序、序列/反序列化、反射
4.1 指令重排序
INSTANCE = new Singleton()
语句并非原子操作,分为以下三步:
- 分配Singleton对象内存空间。
- 初始化对象。
- 将INSTANCE对象引用指向分配的内存空间。
因为指令重排序的问题,以上指令在实际情况可能以1 -> 3 -> 2的顺序执行。
在多线程的情况下可能出现:
执行顺序 | 线程 A | 线程 B |
---|---|---|
1 | 分配Singleton对象内存空间 | |
2 | 将INSTANCE对象引用指向分配的内存空间 | |
3 | 访问getInstance()方法,判断INSTANCE对象是否为null | |
4 | 由于INSTANCE已经完成了对象引用指向,因为不为null,但此时INSTANCE还没有完成初始化 | |
5 | 初始化对象 | |
6 | 访问INSTANCE引用的对象 |
解决办法:
使用volatile修饰INSTANCE对象
public class Singleton {
private volatile static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized(Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
4.2 序列/反序列化破坏单例
参考大佬文章:https://segmentfault.com/a/11...
如果想要防止单例被序列/反序列化破坏。就让单例类实现readResolve()方法。
public class Singleton {
private volatile static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized(Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
private Object readResolve() {
return Singleton.INSTANCE;
}
}
4.3 反射破坏单例
参考大佬文章:https://segmentfault.com/a/11...
为了防止反射破坏单例,采用枚举实现单例模式。
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
参考文献
https://www.liaoxuefeng.com/w...
https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。