标准写法:双重校验锁+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;
}
}
private volatile static Singleton singleton; //volatile 保证单例变量前后不发生指令重排优化 点击链接查看为什么要用volatile在这里禁止指令重排,禁止的是哪里可能的重排?
memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址
根源在于代码中的2和3之间,可能会被重排序。例如:
memory = allocate(); // 1:分配对象的内存空间
instance = memory; // 3:设置instance指向刚分配的内存地址
// 注意,此时对象还没有被初始化!
ctorInstance(memory); // 2:初始化对象
我们判断该对象是否存在的时候是判断该对象有没有指向刚分配的内存地址,如果分配了就认为其不为null,如果我们先执行3再执行2,可能第一个线程刚执行3,第二个线程进来判断外层singleton不为null就直接返回一个还没初始化的对象去使用导致报错.
Singleton getSingleton() 不直接加synchroized锁而是先校验一次后再加锁,这样可以保证该单例具有较高的性能
Singleton为什么设置为static?我们需要提供一个公有的静态方法,将创建的对象返回。 单例类因为不允许其他程序用new来创建该类对象,所以只能将单例类中的方法定义成静态的(随类的加载而加载),静态方法不能访问非静态的成员,故只能将该类中new的本类对象变成静态的。