1、第一版
public class Singleton {
private Singleton() {} //私有构造函数
private static Singleton instance = null; //单例对象
//静态工厂方法
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
存在问题:线程不安全
2、线程安全
双校验单例模式
public class Singleton {
private Singleton() {} //私有构造函数
private static Singleton instance = null; //单例对象
//静态工厂方法
public static Singleton getInstance() {
if (instance == null) { //双重检测机制
synchronized (Singleton.class){ //同步锁
if (instance == null) { //双重检测机制
instance = new Singleton();
}
}
}
return instance;
}
}
存在问题:
2.1、不是绝对安全不安全,原因是指令重排,
增加修饰符volatile阻止变量访问前后的指令重排。
private volatile static Singleton instance = null;
2.2、可通过反射打破单例模式的约束(枚举方式可防止此问题)
静态内部类方式也是线程安全的
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
3、枚举
public enum SingletonEnum {
INSTANCE;
}
- volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。有关volatile的详细原理,我在以后的漫画中会专门讲解。
2.使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。
对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。