创建型之单例模式

前言

  单例模式随处可见,用处也很大,但是写好一个单例模式很不容易,拔出萝卜带出泥,面试也经常会被问到,今天就来总结一下。

  什么是单例?

  1. 单例类只能有1个实例对象,
  2. 单例类必须自己创建该实例,别人不能创建
  3. 单例类必须该所有其他对象提供该实例

1. 饥饿式

/**
 * 1-饿汉式单例类
 */
public class EagerSingleton {

    //private保证实例不被直接访问,static保证只有1份,final保证这1份不被修改
    private static final EagerSingleton instance = new EagerSingleton();

    //private保证不被其它类new出来
    private EagerSingleton(){}

    //静态工厂方法,给其它对象提供相同方法
    public static EagerSingleton getInstance(){
        return instance;
    }
}

  优点:在类被加载的时候,就创建了实例,多线程下是安全的。
  缺点:创建单例的时机过于急躁,在没有用到单例之前,占用了内存,而且初始化加载的配置到最后可能并不是我们想要的

2. 懒汉式

/**
 * 2-懒汉式单例模式
 */
public class LazySingleton {

    //当getInstance方法被调用时才会被创建,没有final修饰,不能保证线程安全
    private static LazySingleton instance = null;

    private LazySingleton(){}

    public synchronized static LazySingleton getInstance() {

        if(instance == null){
            //在多线程情况下,实例的创建会出现现不一致情况,因此要加synchronized
            instance = new LazySingleton();
        }
        return  instance;
    }
}

  优点:保证了线程安全,只有在被用到的情况下,才会初始化实例,节省了空间
  缺点:synchronized加在了方法上,同步过度,每个线程执行getInstance方法时都要同步,执行效率比较低,最好的情况是只在实例第一次初始化时,执行同步

3. 双重检查锁

/**
 * 3-双重检查锁单例模式
 */
public class Singleton implements Serializable {

    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

        //如果实例已经被创建,则不被同步,这样可以提高执行效率
        if (instance == null) {

            //实例初次被创建时,可能有多个线程,所以要加锁,同步
            synchronized (Singleton.class) {

                //实例只可以被创建1次,等其它阻塞线程执行时,不在初始化
                if (instance == null) {
                //该赋值操作,只在当前线程的工作区中执行,什么时候会写到主存,由jvm执行,因此要加volatile,让其它线程可知
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    //防被反序列化时创建多个实例对象
    private Object readResolve() {
        return getInstance();
    }

    //防止通过反射创建多个实例,重写getClass方法
    private static Class getClass(String classname)
            throws ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        if (classLoader == null)
            classLoader = Singleton.class.getClassLoader();

        return (classLoader.loadClass(classname));
    }
}

  优点:理想很丰满,思路清晰,只在初始化实例时,同步
  缺点:现实很残酷,在java旧版本时,支持的并不很好,原因在于instance = new Singleton()在多线程情况下,可能出现构造方法还没执行,就被其它线程拿走了。现如今violate已被优化,可以实现,但是效率并不高

4. 静态内部类

/**
 * 4-内部类单例模式
 */
public class InnerClassSingleton {

    private static class SingletonHolder{
        public static final InnerClassSingleton instance = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance(){
        return SingletonHolder.instance;
    }

}

  优点:由jvm保证线程安全,只有被用到时,才会被初始化
  缺点:序列化和反序列化支持比较差

5. 枚举类

/**
 * 5-枚举单例类
 */
public enum  SingletonEnum {

    INSTANCE; //单例实例
    private DataSource dataSource;
    private SingletonEnum(){

        //初始化单例的内容,添加dataSource的配置内容
    }

    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

  优点:最佳实践,完美解决各种问题
  缺点:项目中运用的少

后记

  能力有限,如有纰漏,请在评论区指出,老朽虽一把年纪,必当感激涕零,泪如雨下。若有满嘴喷粪撕逼者,一律拉黑、举报。
  转载时,注明出处是人格的一种体现(https://www.zybuluo.com/BertLee/note/837611)。
  撰笔之时,参考了以下几篇博文,如有不妥之处,请与老朽联系。

http://blog.csdn.net/huangyuan_xuan/article/details/52193006
http://blog.csdn.net/yangkai_hudong/article/details/50628172
http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html

posted @ 2017-08-03 13:52  吃不了兜着走  阅读(158)  评论(0编辑  收藏  举报