这是我参与「第三届青训营 -后端场」笔记创作活动的第 5 篇笔记
单例可以分成饿汉式和懒汉式(lazy-load)两种。
双校验单例(属于懒汉式)
双校验单例是实际应用中最常用的单例模式,它有两次读操作、一次写操作和一次加锁,切记不要漏了。双校验单例这个模型不仅在 Java 里有应用,在缓存的实现上也会用到这个模型。
public class Singleton<T> {
private volatile T value;
public T get() {
T val = value;// the first line of read
if (val == null) {
synchronized (this) {// lock
val = value;// the second line of read
if (val == null) {
// initialization will only be performed once
value = val = init();// write (only once, even if there are many threads)
}
}// unlock
}
return val;
}
protected T init() {
// override this method to implement the initialization
throw new UnsupportedOperationException();
}
}
枚举单例(属于懒汉式)
枚举单例利用了类加载的特性实现懒加载、能防止反射攻击。 通过反射去 new 一个枚举单例会抛异常:
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
public enum Singleton {
INSTANCE;
public static Singleton get() {
return INSTANCE;
}
/**
* 防止序列化攻击,不能是 static
*/
private Object readResolve() {
return INSTANCE;
}
public void fn() {
// just an example
}
}
Enum 实现了 Serializable ,所以支持序列化,在 ObjectInputStream 里会使用反射去调用 readResolve 方法来作为反序列化结果。
单例防止反射攻击、序列化攻击
防反射攻击可以使用枚举单例,防序列化攻击需实现 readResolve 方法。