美文网首页
你会写哪一种单例模式?

你会写哪一种单例模式?

作者: DreamFish | 来源:发表于2017-07-14 23:34 被阅读0次

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

Singleton:负责创建Singleton类自己的唯一实例,并提供一个getInstance的方法,让外部来访问这个类的唯一实例。

功能

单例模式是用来保证这个类在运行期间只会被创建一个类实例,另外,单例模式还提供了一个全局唯一访问这个类实例的访问点,就是getInstance方法。

饿汉法

顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。代码如下:

    public class Singleton { 
        private static Singleton = new Singleton();
        private Singleton() {}
        public static getSignleton(){
            return singleton;
        }
    }

饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的。但是无法做到延迟创建对象,从而减小负载。

懒汉式

单线程写法

    public class Singleton {
        private static Singleton singleton = null;
        private Singleton(){}
        public static Singleton getSingleton() {
            if(singleton == null) singleton = new Singleton();
                return singleton;
        }
    }

这种不加同步的懒汉式写法虽然简单但同时它是线程不安全的,这种写法由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,如果是null就new一个出来,最后返回singleton对象。这种方法可以实现延时加载,但是有一个致命弱点:线程不安全。

线程安全的写法

如何实现懒汉式的线程安全?加上synchronized?

public static synchronized Singleton getInstance(){}

但这样会降低整个访问的速度,而且每次都要判断。现在比较流行的是用双重检查加锁。

双重加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例。这是第二重检查。

双重加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

    /**
     * 双重检查加锁的单例模式
     * @author dream
     *
     */
    public class Singleton {
    
        /**
         * 对保存实例的变量添加volitile的修饰
         */
        private volatile static Singleton instance = null;
        private Singleton(){
            
        }
        
        public static Singleton getInstance(){
            //先检查实例是否存在,如果不存在才进入下面的同步块
            if(instance == null){
                //同步块,线程安全的创建实例
                synchronized (Singleton.class) {
                    //再次检查实例是否存在,如果不存在才真正的创建实例
                    instance = new Singleton();
                }
            }
            return instance;
        }
        
    }

但是双重加锁机制,一句话说是“成也volatile,败也volatile”,因为volatile关键字在JDK1.5之前无法保证线程安全,所有双重加锁机制也不是完美的,也是有瑕疵的。

一种更好的单例实现方式

静态内部类法,它可以延时加载,并且能保证线程安全,我们把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的。

    public class Singleton {
    
        /**
         * 类级的内部类,也就是静态类的成员式内部类,该内部类的实例与外部类的实例
         * 没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
         * @author dream
         *
         */
        private static class SingletonHolder{
            /**
             * 静态初始化器,由JVM来保证线程安全
             */
            private static Singleton instance = new Singleton();
        }
        
        /**
         * 私有化构造方法
         */
        private Singleton(){
            
        }
        
        public static Singleton getInstance(){
            return SingletonHolder.instance;
        }
    }

优缺点

  • 懒汉式是典型的时间换空间
  • 饿汉式是典型的空间换时间

何时选用单例模式

当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式,这些功能恰好是单例模式要解决的问题。代码没有一劳永逸的写法,只有在特定条件下最合适的写法。在不同的平台、不同的开发环境(尤其是jdk版本)下,自然有不同的最优解(或者说较优解),比如双重检查锁法,不能在jdk1.5之前使用,但是本人是Android开发者,而Android基本都是基于JDK1.6以上开发的,所以我个人偏爱比较双重检查锁法。
当然,还有一种更加优雅的写法:枚举写法,但为什么我没有介绍呢,应为它在Android平台上却是不被推荐的。所以感兴趣的同学可以自行去了解一下。

相关文章

  • 单例模式(Java内部类加载顺序)

    你真的会写单例模式吗——Java实现Android设计模式源码解析之单例模式深度分析 Java 的枚举类型:枚举的...

  • 你真的会写单例吗?

    你真的会写单例吗? 摘录来源 单例的正确姿势 Java单例模式可能是最简单也是最常用的设计模式,一个完美的单例需要...

  • 你会写哪一种单例模式?

    定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 Singleton:负责创建Singleton类自己...

  • 单例模式的优化方案

    参考:你真的会写单例吗? 单例模式最常见的就是懒汉式加载: 例: 当调用getInstance方法时才去创建对象,...

  • Java单例模式

    转载: 你真的会写单例模式吗-------Java实现 单例模式可能是代码最少的模式了,但是少不一定意味着简单,想...

  • 单例模式

    在我们的开发中,有很多地方会用到单例模式,那么会写基本的单例模式使我们基本的素养,如果说现在不会手写单例模式的,那...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • 设计模式

    手写单例模式(线程安全) 你知道几种设计模式?单例模式是什么?Spring中怎么实现单例模式?

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

网友评论

      本文标题:你会写哪一种单例模式?

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