单例模式虽然比较多,但是项目中一般使用饿汉、双重校验锁两种方式。
饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
双重校验锁模式
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) { //1
if (singleton == null) { //2
singleton = new Singleton(); //3
}
}
}
return singleton;
}
}
如果不用volatile,则因为内存模型允许所谓的“无序写入”,可能导致失败。——某个线程可能会获得一个未完全初始化的实例。
考察上述代码中的 //3 行。此行代码创建了一个 Singleton 对象并初始化变量 instance 来引用此对象。这行代码的问题是:在Singleton 构造函数体执行之前,变量instance 可能成为非 null 的!
什么?这一说法可能让您始料未及,但事实确实如此。
在解释这个现象如何发生前,请先暂时接受这一事实,我们先来考察一下双重检查锁定是如何被破坏的。假设上述代码执行以下事件序列:
加上volatile的作用主要是防止指令重排,主要是说,无论什么情况,对于volatile变量的写操作必须在完成后才能读取,所以不会出现未完成构造就读取的情况。
喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!