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;
}
}
这是一段简单的双重校验锁单例模式代码,通过构造器私有化,外部只能通过调用加锁的getInstance获得唯一的instance对象。
但是为什么instance对象必须要用volatile关键字进行修饰?
保证可见性
- volatile 关键字确保了多线程环境中的可见性。当一个线程修改了 instance 的值后,其他线程能够立即看到这个修改。
- 没有 volatile,线程可能会看到过期的 instance 值,导致多个实例的创建。
禁止指令重排
instance对象的 new 过程分为三步:
- 分配内存空间给 instance。
- 初始化 instance。
- 将 instance 的引用设置为新分配的内存地址。
但由于 Java 内存模型允许编译器和处理器重新排序操作以优化性能,这三个步骤可能会被重新排序为:
- 分配内存空间给 instance。
- 将 instance 的引用设置为新分配的内存地址。
- 初始化 instance。
并发时可能会产生意外情况:
添加volatile,以保证单例模式的线程安全性