单例模式

一、懒汉式

public class Singleton {
    private volatile static Singleton instance; //声明成 volatile
    private Singleton (){}

    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

  使用DCL进行锁的双重检查。这里的volatile是亮点:设置内存屏障,防止instance未初始化完就被泄漏出来。(volatile语义不清楚的可能参考:Java并发编程:volatile关键字解析)

 

二、饿汉式

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

  这种写法简单明了,也线程安全。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

 

三、静态内部类

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

  这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

 

四、总结

  一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类。

  

4.1 反射

但以上几种方式都无法保证反射出来还是单例。可以通过如下代码验证:

//获得构造器
Constructor con = Singleton.class.getDeclaredConstructor();
//设置为可访问
con.setAccessible(true);
//构造两个不同的对象
Singleton singleton1 = (Singleton)con.newInstance();
Singleton singleton2 = (Singleton)con.newInstance();
//验证是否是不同对象
System.out.println(singleton1.equals(singleton2));

 

  网上流传一种方法,可以利用enum!有了enum语法糖,JVM会阻止反射获取枚举类的私有构造方法。

public enum SingletonEnum {
    INSTANCE;
}

  

 

4.2 反序列化

反序列化的时候,JVM会根据序列化生成的内容构造新的对象,对于实现了Serializable的单例类来说,这相当于开放了构造方法。为了保证单例类实例的唯一性,我们需要重写resolveObject方法。

resolveObject:反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。readResolve常用来反序列单例类,保证单例类的唯一性。

/**
 * 在反序列化的时候被调用
 * @return 返回根据字节码创建的新对象
 * @throws ObjectStreamException
 */
private Object readResolve()throws ObjectStreamException {
    return instance;
}

  

 

 

参考:

posted @ 2016-05-31 16:26  waterystone  阅读(350)  评论(0编辑  收藏  举报