美文网首页Java设计模式
设计模式——单例模式

设计模式——单例模式

作者: Ant_way | 来源:发表于2018-06-12 16:34 被阅读0次

    设计模式——单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    单例模式在实际的开发过程中,使用最常见。针对频繁使用的类,我们可以将它定义为一个单例来避免类对象频繁的创建销毁,提高效率。单例模式有以下特点:

    • 单例类只能有一个实例
    • 单例类必须自己创建自己的唯一实例
    • 单例类给其它对象提供这一实例
    SingletonPattern.png

    单例模式的实现方式
    围绕单例模式的特点,一般常见的实现方式有以下几种:

    • 懒汉式
    • 饿汉式
    • Double Check Lock(DCL)双重检查锁定
    • 静态内部类实现
    • 枚举单例
    • 记录式单例

    懒汉式
    懒汉式是先声明一个静态类对象,然后在用户第一次调用初始化方法的时候进行初始化。

    懒汉式线程非安全
    这种模式的缺陷就是无法在多线程条件下保证实例只有一个。所以严格意义上来说,在多线程环境下就不能称之为单例模式了。

    /***
     * 懒汉式
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private static Singleton instance;
        private Singleton(){};
        
        public static Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    懒汉式线程安全
    通过对getInstance方法添加同步方法锁的形式来达到线程同步,每次调用getInstance方法都会进行同步,这样在单线程环境下效率很低。

    /***
     * 懒汉式线程安全
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private static Singleton instance;
        private Singleton(){};
        
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    饿汉式
    在单例对象声明的时候即进行实例化。

    /***
     * 饿汉式
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){};
        
        public static synchronized Singleton getInstance(){
            return instance;
        }
    }
    

    这种方式避免了在多线程环境下执行getInstance的问题,但是在不同的类加载器的环境下也可能出现不同的实例。

    DCL模式(双重检查锁定)
    双重检查锁定是通过多次判断instance是否为null来实现,然后结合volatile关键字来实现。

    /***
     * DCL模式
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private volatile static Singleton instance;
        private Singleton(){};
        
        public static Singleton getInstance(){
            if(instance == null){
                synchronized (Singleton.class) {
                    if(instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    双重检查锁定模式实现的亮点是在getInstance方法的实现以及实例的定义上。它通过两次判断null值进行实现。在第一次判断主要是为了不必要的锁机制,第二次判断是为了确保在null的情况下进行创建实例。这里使用volatile关键字来定义instance就是确保能够保持一致性。DCL模式资源利用率高,只有在第一次调用getInstance时才会继续拧初始化操作。

    静态内部类

    /***
     * 静态内部类模式
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private Singleton(){};
        
        public static synchronized Singleton getInstance(){
            return SingleBuilder.instance;
        }
        
        private static class SingleBuilder{
            private static final Singleton instance = new Singleton();
        }
    }
    

    这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,即使Singleton类被装载了,instance不一定被初始化,因为SingleBuilder类没有被主动使用,只有显示通过调用使用实例时,才会显示装载SingleBuilder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的,所非常推荐这种方式。

    枚举单例
    枚举的每个实例在Java堆中有且只有一个副本,这种特性让枚举很容易就实现了单例模式。

    enum SingletonEnum{
        INSTANCE;
        
        
        public void drawCircle(){
            System.out.print("drawCircle");
        }
    }
    
        public static void main(String[] args) {
            SingletonEnum.INSTANCE.drawCircle();
        }
    

    这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,非常推荐使用。枚举在Java中和普通类一样,可以拥有自己的字段和方法,最重要的是枚举的实例创建是线程安全的,任何情况下都是一个单例。

    记录式单例
    记录式单例就是通过自定义管理器然后记录管理实例的创建创建,以此来保证单例。

    /***
     * 记录式单例
     * @author Iflytek_dsw
     *
     */
    public class Singleton {
        private static Map<String,Object> keyMap = new HashMap<>();
        private Singleton(){};
        
        public static Singleton getInstance(String key){
            return (Singleton) keyMap.get(key);
        }
        
        public static void registerSingle(String clazzName,Object obj){
            if(keyMap.get(clazzName) == null){
                keyMap.put(clazzName, obj);
            }
        }
    }
    

    记录式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

    总结
    不管以哪种方式实现单例模式,核心都是私有化构造函数,并且通过一个静态方法获取单个实例,这个过程中出现的变种就是要保证线程是否安全、放置序列化导致新生成对象的问题。选择哪种方式取决于使用环境:是否是多线程环境、性能要求、JDK版本等。

    相关文章

      网友评论

        本文标题:设计模式——单例模式

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