美文网首页
单例模式及最佳实践

单例模式及最佳实践

作者: 意梦春秋 | 来源:发表于2018-10-16 10:04 被阅读0次

    写在最前面


    单例模式是一个非常容易理解,也非常容易实现和使用的设计模式,但是我们需要足够小心的去使用它,绝大部分情况下,使用单例模式都不是一个好的选择。推荐仅用于了解设计模式的思想。

    什么是单例模式?

    单例对象的类必须保证只有一个实例存在

    构造对象的方法

    • 构造器 new Instance()
    • 序列化反序列化获取

    防止多次构造对象

    • 防止通过构造器获取对象
      • 将构造器设为private 防止直接调用
      • 手动构造一个实例固定返回
      • 问题:多线程安全和通过反射构造实例
    • 防止通过序列化反序列化多次获取实例

    常见单例实现方式优缺点

    • 饿汉模式

      • 在静态代码块中构造唯一的实例 示例代码如下
        public class Singleton{
          private static Singleton instance = new Singleton();
          private Singleton(){}
          public static Singleton newInstance(){
              return instance;
          }
        }
        
         public class Singleton{
          private static Singleton instance;
          static{
                instance=new Singleton();
          }
          private Singleton(){}
          public static Singleton newInstance(){
              return instance;
          }
        }
        
        优点: 线程安全(why?见文末) 实现较为简单
        缺点: 相较于懒汉模式,加载压力大。没有解决反射和序列化的问题
    • 懒汉模式

      • 在获取时判断,为空则构造实例

        //V1.0
        public class Singleton{
           private static Singleton instance = null;
           private Singleton(){}
           public static Singleton newInstance(){
               if(null == instance){
                   instance = new Singleton();
               }
               return instance;
           }
        }  
        

        如上代码多线程下可能创建多个实例,即轮流判断是否为null都进入if语句进行初始化 最简单的思路 上锁!

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

        太慢了,如果多个线程同时获取,效率很低。著名的double-check模式

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

        看起来超完美了,懒加载线程安全效率也高,然而,由于jvm的指令重排序功能,导致这段代码仍然存在问题。对象的创建分为三个步骤

        • 分配空间并赋默认值
        • 对象初始化 <init>函数 即构造器
        • 地址分配 即建立引用关系

        除了第一步是固定在最早发生的,后面两步的顺序不是固定的,而判断对象是不是null只需要第三步完成就不是null了,可能这时候还没有完成对象的初始化过程,使用的过程中就会出错!
        想要取消指令重排序就要用到jdk1.5以后推出的volatile关键字,这个关键字会形成内存栅栏防止前后重排序。所以,最终版代码如下

        public class Singleton {
            private static volatile Singleton instance = null;
            private Singleton(){}
            public static Singleton getInstance() {
                if (instance == null) {
                    synchronized (Singleton.class) {
                        if (instance == null) {
                            instance = new Singleton();
                        }
                    }
               }
                return instance;
            }
        }
        
    • 静态内部类
      通过类加载机制同时达到线程安全和完成懒加载(内部类只有在被调用时才加载)

      public class Singleton{
          private static class SingletonHolder{
              public static Singleton instance = new Singleton();
          }
          private Singleton(){}
          public static Singleton newInstance(){
              return SingletonHolder.instance;
          }
      }
      
    • 枚举 (推荐方式但应用较少)

      public enum Singleton{
          INSTANCE;
      }
      

      具有以下优点

      • 线程安全 枚举实际上元素都是static final类型的在类加载时完成
      • 能够抵抗反射的攻击


        枚举类构造函数
      • 能够抵抗序列化攻击
        枚举类的序列化函数是使用valueOf()取出的枚举类型的名字,而反序列化也是根据名字来找到对应的实例,所以序列化也不会产生额外的对象

      缺点:

      • 不是懒加载

    为什么类加载是线程安全的?

    虚拟机类加载器在类初始化时会给<clinit>()方法上锁保证线程安全,所以如果<clinit>()方法中有耗时操作,可能会产生难以发现的阻塞。

    注意:其他线程阻塞结束后不会进入<clinit>()方法,保证只执行一次

    相关文章

      网友评论

          本文标题:单例模式及最佳实践

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