枚举来实现单例

双重校验锁 实现单例:

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() {  
    }  
} 

上面的双重锁校验的代码很臃肿,是因为大部分代码都是在保证线程安全。为了在保证线程安全和锁粒度之间做权衡,代码难免会写的复杂些。但是,这段代码还是有问题的,因为他无法解决 反序列化会破坏单例 的问题。

那么,为什么使用枚举就不需要解决线程安全问题呢?

其实在 底层 还是做了线程安全方面的保证的。因为虚拟机在加载枚举的类的时候,会使用 ClassLoaderloadClass 方法,而这个方法使用同步代码块保证了线程安全,所以,创建一个 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 方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve 等方法。

枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。

public static > T valueOf(Class 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);  
} 

你可能感兴趣的:(枚举来实现单例)