天天看点

回顾一下单例模式

       在Java的23中设计模式中,单例模式是最简单也是最常考的一个设计模式,实现单例模式的方式有很多种,而且有的实现方式会考虑线程安全和反射机制的破坏,其中最常见的不外乎​

​懒汉模式​

​​、​

​饿汉模式​

​​、​

​双重检查式​

​​和​

​枚举式​

​,加下来就每个方式的单例模式进行代码的实现,以此来做一个对比。

1 懒汉模式

       顾名思义,懒汉就是在需要的时候去做事情,因此懒汉模式就是​

​在需要的时候再去new​

​,进行对象的实例化,等再次需要时先进行检查,如果对象不为空就直接返回。

/**
 * 懒汉式
 *
 * @author 17122
 */
public class LanHanSingleton {

    /**
     * 先声明,不需要new
     */
    private static LanHanSingleton instance;

    /**
     * 构造方法私有,不允许new
     */
    private LanHanSingleton() {
    }

    /**
     * 多线程环境下加入synchronized可保障线程安全
     *
     * @return
     */
    public static synchronized LanHanSingleton getInstance() {
        //检查如果对象为空则重新创建
        if (instance == null) {
            instance = new LanHanSingleton();
        }
        return instance;
    }
}      

2 饿汉模式

       可以把饿汉当做JVM的类加载器,而需要实例化的对象就是食物,当在JVM类加载时就迫切的进行类的实例化,因此饿汉模式名由此而生。简单的描述就是在类加载时将对象实例化,获取时直接返回。

/**
 * 饿汉式单例
 *
 * @author 17122
 */
public class EHanSingleton {
    /**
     * 初始化类时先创建实例
     */
    private static EHanSingleton instance = new EHanSingleton();

    /**
     * 构造方法私有,不允许new
     */
    private EHanSingleton() {
    }

    public static EHanSingleton getInstance() {
        //返回实例
        return instance;
    }
}      

3 双重检查式

       双重检查式的实现主要是为了多线程下的安全状态,放在多个线程在第一次if判断时产生相同的结果。而且在第一次if之后加入了对象锁,确保多线程下线程的安全。

/**
 * 双重检查式
 *
 * @author 17122
 */
public class DoubleCheckSingleton {

    /**
     * volatile保证多线程下数据的可见性
     */
    private volatile static DoubleCheckSingleton singleton;

    /**
     * 构造方法私有,不允许new
     */
    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getSingleton() {
        //第一次检查
        if (singleton == null) {
            //加入对象锁
            synchronized (DoubleCheckSingleton.class) {
                //第二次检查
                if (singleton == null) {
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}      

4 枚举式

       这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

/**
 * 枚举式
 *
 * @author 17122
 */
public enum EnumSingleton {

    INSTANCE;

    /**
     * 返回实例
     *
     * @return
     */
    public static EnumSingleton getInstance() {
        return INSTANCE;
    }

    //以下可以时任意方法

}      

5 对比

是否线程安全 是否能被反射破坏 多线程下使用效率
懒汉模式 较低
饿汉模式 较高
双重检查式 较高
枚举式

6 扩展:反射如何破坏单例?

/**
 *  反射破坏单例模式
 */
try {
    DoubleCheckSingleton singleton = DoubleCheckSingleton.getSingleton();
    Constructor<DoubleCheckSingleton> constructor = DoubleCheckSingleton.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    DoubleCheckSingleton instance = constructor.newInstance();
    System.out.println("反射破坏单例:");
    System.out.println(singleton == instance);
} catch (Exception e) {
    e.printStackTrace();
}