美文网首页编程学习程序员移动开发
你真的会用单例模式吗?

你真的会用单例模式吗?

作者: 唠嗑008 | 来源:发表于2017-01-13 23:16 被阅读918次

    一、简介:

     我们经常有这样的需求:  某一些类应该只存在一个实例  的时候,我们就可以用单例模式来应对.
    

    单例模式:确保一个类只有一个实例,并提供一个全局访问点.

    单例模式是所有设计模式中最简单的一个,也是大部分人最早知道的一个设计模式.

    二、我们经常用的2种单例模式(懒汉式、饿汉式)

    (1)饿汉式:

    饿汉式单例类.在类初始化时,已经自行实例化

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

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的。

    (2)懒汉式

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

    备注


    Paste_Image.png
     单例模式的懒汉式实现方式体现了延迟加载的思想,什么是延迟加
    载呢?  通俗点说,就是一开始不要加载
    资源或者数据,一直等,等到马上就要使用这个资源或者数据了,躲不过去了才加载,所以也称
    Lazy Load,不是懒惰啊,是“延迟加载”,这在实际开发中是一种很常见的思想,尽可能的节约资源。
    
       单例模式的懒汉式实现还体现了缓存的思想,缓存也是实际开发中非常常见的功能。简单讲就是,如果
    某些资源或者数据会被频繁的使用,而这些资源或数据存储在系统外部,比如数据库、硬盘文件等,
    那么每次操作这些数据的时候都从数据库或者硬盘上去获取,速度会很慢,会造成性能问题。 一个简单的
    解决方法就是:把这些数据缓存到内存里面,每次操作的时候,先到内存里面找,看有没有这些数据,
    如果有,那么就直接使用,如果没有那么就获取它,并设置到缓存中,下一次访问的时候就可以直接从内存
    中获取了。从而节省大量的时间,当然,缓存是一种典型的空间换时间的方案。
    

    两种单例模式的比较

    比较上面两种写法:
    1、懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,费判断的时间,当然,如果一直没有人使用的话,那就不会创建实例,节约内存空间。
    2、 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。

    (3)双重检查加锁

    public class Singleton {        
    
            private volatile static Singleton instance = null;
            private Singleton() {}
            public static Singleton getInstance() {
                //先检查实例是否存在,如果不存在才进入下面的同步块
                if (instance == null) {
                    //同步块,线程安全的创建实例
                    synchronized (Singleton.class) {
                        //再次检查实例是否存在,如果不存在才真的创建实例
                        if (instance == null) {
                            instance = new Singleton();
                        }
                    }
                }
                return instance;
            }
    

    双重检查详解
    可以使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受到大的影响。
    那么什么是“双重检查加锁”机制呢? 所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。 双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。 这种实现方式既可使实现线程安全的创建实例,又不会对性能造成太大的影响,它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快运行速度。 注意:在Java1.4及以前版本中,很多JVM对于volatile关键字的实现有问题,会导致双重检查加锁的失败,因此双重检查加锁的机制只能用在Java5及以上的版本。

    (4)、静态内部类单例模式

    public class Singleton {
    
            private static class Holder{
                private static Singleton INSTANCE = new Singleton();
            }
            public static Singleton getInstance(){
                return Holder.INSTANCE;
            }
        }
    

    当第一次加载Singleton类时并不会初始化INSTANCE,只有在第一次调用getInstance方法时才会导致INSTANCE被初始化。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延长了单例的实例化。

    小结

    1、双重检查非常适用于高并发,我们熟知的开源库Eventbus,ImageLoader等都是用的双重检查锁方式实现单例

    2、单例模式是运用频率很高的模式,但是,由于在客户端通常没有高并发的情况,因此,选择哪种实现方式都不会有太大的影响。即使如此,出于效率考虑,推荐使用DCL单例(双重检查锁定)和静态内部类单例模式。

    相关文章

      网友评论

      • aad089bfd751:枚举单例呢?
      • 竿牍:看标题,我以为是说单例模式在哪些场景下使用。

        文章内容只是告诉说写法,
      • c072c1d1f253:满详细的,开始看代码的时候发现好像有点问题感觉像只写了一半的样子,后来发现那个图片可以左右移动,哈哈。点赞
        唠嗑008: @小胡的Singer 有些东西吧,不用非要扣细枝末节,能获取到对自己有用的关键部分就好,互相学习
      • 冰鉴IT:字写错了,说实话有点误人子弟
      • Wilstro:饿汉式的代码没对吧
      • 大数据小白白:误人子弟
      • wxyjj:前面我都想着要不要写一篇单例模式,现在看到你这篇挺不错的,加油。😀
        唠嗑008: @locality 互相学习!
      • 为什么一定要起昵称:所以应该怎么用?
        唠嗑008: @为什么一定要起昵称 谢谢!
        为什么一定要起昵称:@陪你唠嗑 http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html看看这里的单例模式
        唠嗑008:@为什么一定要起昵称 个人比较喜欢后2种!
      • 6b5e74981e77:非常详细,:+1:🏻
        唠嗑008:@zhy_nju 互相学习,共同进步!

      本文标题:你真的会用单例模式吗?

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