Java中单例模式是一种广泛使用的设计模式。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 饿汉式
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
- 懒汉式
class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
- 双重检查锁(Double-Checked Locking, DCL)
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;
}
}
为什么必须用 volatile?
问题出在 instance = new Singleton(); 这行代码。它并非一个原子操作,在 JVM 中大致分为三步:
- 为对象分配内存空间。
- 初始化对象(调用构造函数等)。
- 将 instance 引用指向这块内存。
JVM 可能会进行指令重排序,将步骤 2 和 3 调换顺序。即:先分配内存并让 instance 指向它,然后再初始化。
考虑以下场景:
- 线程 A 进入同步块,执行 new Singleton()。JVM 重排序后,先执行了步骤 1 和 3。此时 instance 已不为 null,但对象还未初始化。
- 此时线程 B 执行第一次检查 if (instance == null),发现 instance 不为 null,于是直接返回这个尚未初始化完成的实例。线程 B 使用这个不完整的对象就会出错。
volatile 的作用:
- 禁止指令重排序: 确保 instance = new Singleton() 的操作按 1->2->3 的顺序执行。
- 保证可见性: 当一个线程修改了 volatile 变量,新值会立即被刷新到主内存,其他线程能立刻看到。因此,volatile 是 DCL 模式不可或缺的一部分。
- 静态内部类
class Singleton{
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
2335

被折叠的 条评论
为什么被折叠?



