我们知道普通“懒汉式”的单例模式,当多线程调用时,他们可能都试图同时创建对象,或者可能最终获得对未完全初始化对象的引用。为了处理这个问题,使用了双重检查锁(DCL)的方式优化了懒汉式,代码如下:
public class LazyMan {
private LazyMan() {
}
private static LazyMan lazyMan;
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
看起来好像实现了比较靠谱的单例模式,以下调用接口为true
LazyMan lazyMan = LazyMan.getInstance();
LazyMan lazyMan2 = LazyMan.getInstance();
System.out.println(lazyMan == lazyMan2);
但是,我们可以确保通过反射机制调用私有的构造器,达到破坏单例的目的:
尝试反射方式破坏单例:
LazyMan lazyMan = LazyMan.getInstance();
//反射
Constructor<LazyMan> lazyManConstructor = LazyMan.class.getDeclaredConstructor();
//成员变量为private,故必须进行此操作
lazyManConstructor.setAccessible(true);
LazyMan lazyMan2 = lazyManConstructor.newInstance();
System.out.println(lazyMan == lazyMan2);
结果返回false,单例被破坏。
可以在构造函数里加锁加判断:
private LazyMan() {
synchronized (LazyMan.class) {
if (lazyMan != null) {
throw new RuntimeException("不要用反射破坏单例");
}
}
}
第二次获得实例的时候就会报错:java.lang.RuntimeException: 不要试图用反射破坏单例
既然单例的对象被加锁,可以在设置第三方标志位:
private static boolean flag = false;
private LazyMan() {
synchronized (LazyMan.class) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("不要用反射破坏单例");
}
}
}
既然private构造方法可以被反射破坏,private的标志位也可以被更改:
//反射
Constructor<LazyMan> lazyManConstructor = LazyMan.class.getDeclaredConstructor();
lazyManConstructor.setAccessible(true);
LazyMan lazyMan = lazyManConstructor.newInstance();
Field flag = LazyMan.class.getDeclaredField("flag");
flag.setAccessible(true);
flag.set(lazyMan, false);
LazyMan lazyMan2 = lazyManConstructor.newInstance();
System.out.println(lazyMan == lazyMan2);
单例再次被破坏。
当然也有其他的静态内部类的方式,但从上面private被多次破坏可以确认:私有并不安全!
从JDK1.5开始,实现Singleton推荐使用枚举的方式,简单安全:
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance() {
return INSTANCE;
}
}
我们也尝试用反射破坏:
Constructor<EnumSingleton> enumSingletonConstructor = EnumSingleton.class.getDeclaredConstructor();
enumSingletonConstructor.setAccessible(true);
EnumSingleton enumSingleton = enumSingletonConstructor.newInstance();
报错没有无参构造方法:java.lang.NoSuchMethodException: singleton.EnumSingleton
再次尝试,尝试有参构造方法:
Constructor<EnumSingleton> enumSingletonConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
报错:Cannot reflectively create enum objects
得到结论:枚举的类没有无参构造方法,故不能被反射破坏