枚举来实现单例
2022-06-17 本文已影响0人
Drew_MyINTYRE
双重校验锁 实现单例:
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;
}
}
枚举 实现单例:
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
上面的双重锁校验的代码很臃肿,是因为大部分代码都是在保证线程安全。为了在保证线程安全和锁粒度之间做权衡,代码难免会写的复杂些。但是,这段代码还是有问题的,因为他无法解决 反序列化会破坏单例 的问题。
那么,为什么使用枚举就不需要解决线程安全问题呢?
其实在 底层 还是做了线程安全方面的保证的。因为虚拟机在加载枚举的类的时候,会使用
ClassLoader
的loadClass
方法,而这个方法使用同步代码块保证了线程安全,所以,创建一个 enum 类型是线程安全的。
public enum T {
SPRING, SUMMER, AUTUMN, WINTER;
}
反编译后代码为:
public final class T extends Enum
{
...
public static final T SPRING;
public static final T SUMMER;
public static final T AUTUMN;
public static final T WINTER;
private static final T ENUM$VALUES[];
static
{
SPRING = new T("SPRING", 0);
SUMMER = new T("SUMMER", 1);
AUTUMN = new T("AUTUMN", 2);
WINTER = new T("WINTER", 3);
ENUM$VALUES = (new T[] {
SPRING, SUMMER, AUTUMN, WINTER
});
}
}
枚举可解决反序列化会破坏单例的问题
对于序列化这件事情,为什么枚举又有先天的优势了呢?
在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的
valueOf
方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject
、readObject
、readObjectNoData
、writeReplace
和readResolve
等方法。
枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum const " + enumType +"." + name);
}