美文网首页
单例模式

单例模式

作者: CodingSnail | 来源:发表于2017-12-06 13:26 被阅读0次
    微信图片_20171218200747.jpg

    目前代码已经上传到GitHub,里面有设计模式系列的整个代码
    GitHub 设计模式地址

    1、什么是单例模式?

    确保一个类只有一个实例,而且自行实例化并向整个系统提供一个全局访问点。

    2、单例模式的优点:

    在内存中只有一个对象,节省内存空间。
    避免频繁的创建销毁对象,可以提高性能。
    避免对共享资源的多重占用。
    可以全局访问。

    3、单例模式的常见的写法

    饿汉、懒汉、懒汉安全、DCL、枚举、容器、静态内部类

    饿汉模式

    /**
     * Created by Snail on 12/6/2017 11:02 AM
     * Contact with slowsnail0223@gmail.com
     * 饿汉模式
     */
    public class HungrySingleton {
    
        private static final HungrySingleton instance = new HungrySingleton();
    
        private HungrySingleton() {
        }
    
        public static HungrySingleton getInstance() {
            return instance;
        }
    }
    

    线程安全的,但是声明的时候已经初始化了,对于一些不是很常用的,会消耗一定的资源。

    懒汉模式

    /**
     * Created by Snail on 12/6/2017 10:54 AM
     * Contact with slowsnail0223@gmail.com
     * 懒汉模式
     */
    public class LazySingleton {
    
        private static LazySingleton instance;
    
        private LazySingleton() {}
    
        public static LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }
    

    懒汉模式 只有使用时才会实例化,在一定程度上节约了资源,缺点是第一次加载时需要及时进行实例化,反应稍慢,线程不安全

    饿汉安全模式

    /**
     * Created by Snail on 12/6/2017 10:56 AM
     * Contact with slowsnail0223@gmail.com
     * 懒汉模式 方法同步锁
     */
    public class LazySafetySingleton {
    
        private static LazySafetySingleton instance;
    
        private LazySafetySingleton() {
        }
    
        public static synchronized LazySafetySingleton getInstance() {
            if (instance == null) {
                instance = new LazySafetySingleton();
            }
            return instance;
        }
    }
    

    getInstance()添加了synchronized关键字,即使instance已经初始化了,那么下次在访问的时候还是会进行同步,这样会消耗不必要的资源。

    DCL模式

    /**
     * Created by Snail on 12/6/2017 11:01 AM
     * Contact with slowsnail0223@gmail.com
     * Double Check Lock
     */
    public class DclSingleton {
    
        private volatile static DclSingleton instance = null;
    
        private DclSingleton() {
        }
    
        public static DclSingleton getInstance() {
            if (instance == null) {
                synchronized ((DclSingleton.class)) {
                    if (instance == null) {
                        instance = new DclSingleton();
                    }
                }
            }
            return instance;
        }
    }
    

    DCL方式实现单例的优点是既能在需要时才初始化单例,又能够保证线程安全,且单例对象在初始化后调用getInstance()不进行同步锁,但是也有需要注意的地方。
    instance = new DclSingleton()编译成汇编指令的时候,大概做了三件事:
    1、给DclSingleton 的实例分配内存
    2、调用DclSingleton()的构造函数,初始化成员字段
    3、将instance对象指向分配的内存空间
    但是由于Java编译器允许处理器乱序执行,上面的2和3的顺序是无法保证的,可能是1-2-3,也可能是1-3-2,如果是后者,并且在3执行完毕,2执行之前,被切换到线程B上,这时候instance在A内已经制定过了第三点,instance已经是非空了,所有线程B直接取走instance,再使用时就会出错,这就是DCL失效问题,而且这种难以跟踪和重现。
    在JDK1.5之后,SUN官方注意到这种问题,调整了JVM,具体化了volatile关键字, private volatile static DclSingleton instance = null;就可以保证instance对象每次都是从主内存中读取。
    DCL的有点:资源利用率高,第一次执行getInstance()时单例对象才会被初始化,效率高。缺点:
    第一次加载的时候反应稍慢,也由于Java内存模型的原因偶尔会失败,在高并发的环境下有一定的缺陷,DCL是使用最多的单例模式实现方式。

    枚举模式

    /**
     * Created by Snail on 12/6/2017 11:04 AM
     * Contact with slowsnail0223@gmail.com
     * 枚举模式
     */
    public enum EnumSingleton {
    
        INSTANCE;
    
        public void doSomething() {
        }
    }
    

    写法简单是枚举单例的最大的优点,枚举在Java中与普通的类是一样的,不仅能够有字段还能够有自己的方法,最重要的事默认枚举实例创建的单例是线程安全的,上面几种通过反序列化会重现重新穿件对象的情况,通过反序列化将一个单例的实例对象写入到磁盘,然后再堵回来,从而有效的获得一个实例。

    静态内部类模式

    /**
     * Created by Snail on 12/6/2017 11:10 AM
     * Contact with slowsnail0223@gmail.com
     * 静态内部类
     */
    public class StaticInnerSingleton {
    
    
        private StaticInnerSingleton() {
        }
    
        public static StaticInnerSingleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    
        private static class SingletonHolder {
            private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
        }
    }
    

    当第一次加载StaticInnerSingleton 类时并不会初始化instance,只有第一次调用StaticInnerSingleton 的getInstance()方法才会导致instance被初始化,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,是最推荐的单例实现模式。

    容器单例模式

    /**
     * Created by Snail on 12/6/2017 11:20 AM
     * Contact with slowsnail0223@gmail.com
     * 容器单例模式
     */
    public class MapSingleton {
    
        private static Map<String, Object> map = new HashMap<>();
    
        private MapSingleton() {
        }
    
        public static void registerSingleton(String key, Object instance) {
            if (!map.containsKey(key)) {
                map.put(key, instance);
            }
        }
    
        public static Object getSingleton(String key) {
            return map.get(key);
        }
    }
    

    在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象,这种方式可以方便的管理多种类型的单例,并且可以使用时通过统一的接口进行获取操作,降低了用户的成本,也对用户隐藏了具体实现,降低了耦合度。

    Kotlin中的单例模式

    /**
     * Created by Snail on 12/15/2017 1:18 PM
     * Contact with slowsnail0223@gmail.com
     */
    class KotlinSingleton {
    
        public var value: KotlinSingleton? = null
    
        private object mHolder {
            val INSTANCE = KotlinSingleton()
        }
    
        companion object Factory {
            fun getInstance(): KotlinSingleton {
                return mHolder.INSTANCE
            }
        }
    }
    

    不管是哪种方式实现单例模式,它们的核心原理就是将构造函数私有化,并且通过静态方法获取唯一一个实例,在这个获取的过程中必须保证线程安全,防止反序列化导致重新生成实例对象。

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/gjdkixtx.html