懒汉模式的单例模式一般会有如下写法
public class Singleton {
private static Singleton singleton;
private Singleton (){
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
在getInstance的时候,先判断singleton是否为null,避免不必要的同步锁,造成资源浪费,同时保证在null的情况下,synchronized执行new新对象,表面看起来没什么问题,实际上在某些特定情况下,singleton可能直接返回一个null。
因为singleton = new Singleton(); 不是一个原子性的操作,这句代码最终被编译成多条汇编指令,大致做了如下3件事情
- 给singleton分配内存
- 调用Singleton的构造函数,初始化成员字段
- 将sInstance对象指向分配的内存空间(此时sInsance就不是null了)
由于java编译器允许处理器乱码执行,上面的2,3两部执行顺序是不确定的,可能是1-3-2或者1-2-3,如果是前者,并且在3执行完成2未执行的时候,切换到线程B形成去执行getInstance,B线程就会直接判断singleton不为null,但这个时候实际上singleton是null的,再使用就会出错。
那么怎么解决这个问题呢?很简单volatile关键字,修改后的实现如下
public class Singleton {
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;
}
}