美文网首页
设计模式之单例模式

设计模式之单例模式

作者: Skypew | 来源:发表于2017-11-15 17:52 被阅读6次

    参考地址:

    应用最广的模式-单列模式(结合Android源码) -----微信http://mp.weixin.qq.com/s/ZfJPhEPp-NaY5ZFsBZzyYw

    android设计模式源码分析
    https://github.com/simple-android-framework/android_design_patterns_analysis

    Android中常用的2种模式

    • 恶汉式
    public class Singleton{
        //严谨考虑 final 需要写(可以不写) 
        private static final Singleton singleton = new Singleton();
        
        public static Singleton getInstance(){
            return singleton;
        }
        
        private Singleton(){
        
        }
    }
    
    • 懒汉式(延迟加载) 升级 双重检查单例( DCL 实现单例)
    public class SingletonClass{
    
        private static SingletonClass instance = null;
    
        public static SingletonClass getInstance(){
           //双层判断 
            if(instance == null){
                synchronized(SingletonClass.class){
                    if(instance == null){
                    instance = new SingletonClass();
                    }
                }
            }    
            return instance;
        }
        private SingletonClass(){
        
        } 
    }
    
    

    还会报错的情况

    创建一个变量需要哪些步骤呢?一个是申请一块内存,调用构造方法进行初始化操作,另一个是分配一个指针指向这块内存。这两个操作谁在前谁在后呢?JVM规范并没有规定。那么就存在这么一种情况,JVM是先开辟出一块内存,然后把指针指向这块内存,最后调用构造方法进行初始化。

    下面我们来考虑这么一种情况:线程A开始创建SingletonClass的实例,此时线程B调用了getInstance()方法,首先判断instance是否为null。按照我们上面所说的内存模型,A已经把instance指向了那块内存,只是还没有调用构造方法,因此B检测到instance不为null,于是直接把instance返回了——问题出现了,尽管instance不为null,但它并没有构造完成,就像一套房子已经给了你钥匙,但你并不能住进去,因为里面还没有收拾。此时,如果B在A将instance构造完成之前就是用了这个实例,程序就会出现错误了!

    解决方案

    在JDK 5之后,Java使用了新的内存模型。

    当然这个bug已经修复了,SUN官方调整了JVM,具体了Volatile关键字,因此在jdk1.5之前只需要写成这样既可, private Volatitle static Singleton instance; 这样就可以保证每次都是从主内存中取,当然这样写或多或少的回影响性能,但是为了安全起见,这点性能牺牲还是值得。

    public class SingletonClass { 
       //volatile  避免出现单例失败的情况 jdk1.5以前
      private volatile static SingletonClass instance = null; 
    
      public static SingletonClass getInstance() { 
        if (instance == null) { 
          synchronized (SingletonClass.class) { 
            if(instance == null) { 
              instance = new SingletonClass(); 
            } 
          } 
        } 
        return instance; 
      } 
    
      private SingletonClass() { 
    
      } 
        
    }
    

    静态内部类的方式

    然而,这只是JDK1.5之后的Java的解决方案,那之前版本呢?其实,还有另外的一种解决方案,并不会受到Java版本的影响:

    public class SingletonClass { 
        
      private static class SingletonClassInstance { 
        private static final SingletonClass instance = new SingletonClass(); 
      } 
    
      public static SingletonClass getInstance() { 
        return SingletonClassInstance.instance; 
      } 
    
      private SingletonClass() { 
    
      } 
        
    }
    

    在这一版本的单例模式实现代码中,我们使用了Java的静态内部类。这一技术是被JVM明确说明了的,因此不存在任何二义性。在这段代码中,因为SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。

    由于SingletonClassInstance是私有静态内部类,所以不会被其他类知道,同样,static语义也要求不会有多个实例存在。并且,JSL规范定义,类的构造必须是原子性的,非并发的,因此不需要加同步块。同样,由于这个构造是并发的,所以getInstance()也并不需要加同步。

    至此,我们完整的了解了单例模式在Java语言中的时候,提出了两种解决方案。个人偏向于第二种,并且Effiective Java也推荐的这种方式。

    相关文章

      网友评论

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

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