1. 非lazy模式
//Singleton with static factory
class SingletonExample {
private static final SingletonExample singleton = new SingletonExample();
private SingletonExample() {
}
public static final SingletonExample getInstance() {
return singleton;
}
}
2. lazy模式的实现 (效率很低)
class SingletonExample {
private static SingletonExample singleton;
private SingletonExample() {
}
public synchronized SingletonExample getInstance() {
if (singleton == null) {
singleton = new SingletonExample();
}
return singleton;
}
}
3. 高效的lazy模式的实现
class SingletonExample {
private static SingletonExample singleton;
private SingletonExample() throws Exception {
}
public static SingletonExample getInstance() {
if (singleton == null) { // the calls arriving after singleton is created will not enter this branch, this line is not synchronized, so they won’t lock each other.
synchronized (SingletonExample.class) { // for the first call, we still have to lock it
if (singleton == null) {
try {
singleton = new SingletonExample();
} catch (Exception e) {
}
}
}
}
return singleton;
}
}
4. 以上所有设计在两种情况下都及其脆弱:
其一,Java Reflection attack,比如:
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<?> clazz = Class.forName("test.SingletonExample");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonExample singleton1 = (SingletonExample)constructor.newInstance();
SingletonExample singleton2 = (SingletonExample)constructor.newInstance();
}
其二,如果单例需要序列化,在反序列化的时候单例会被破坏。
5. 推荐的解决方案使用 enum代替class来写单例
public enum SingletonExample {
INSTANCE;
public static SingletonExample getInstance() {
return INSTANCE;
}
}
按照Effective Java的结论,这种方法 简介,使用了由JDK为enum提供的特殊的序列化机制,在面对复杂的序列化和反序列化的时候能够有效的防止被多次实例化。另外enum的的构造器一定是private的,而且其构造器仅对内部成员可见,在运行时外部类是没办法看到这个构造器的(比private严格得多),所以无法利用常规反射手段将其设为public.