zoukankan      html  css  js  c++  java
  • 设计模式系列(一)单例模式

    1.单例模式

    这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类 ,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式, 可以直接访问,不需要实例化该类的对象。

    • 单例类只能有一个实例。
    • 单例类必须自己创建自己的唯一实例。
    • 单例类必须给所有其他对象提供这一实例。

    意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
    主要解决:一个全局使用的类频繁地创建与销毁。
    何时使用:当您想控制实例数目,节省系统资源的时候。
    如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
    关键代码:构造函数是私有的。

    优点
    1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
    2. 避免对资源的多重占用(比如写文件操作)。
    缺点

    没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    使用场景:
    1. 要求生产唯一序列号。
    2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
    3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

    注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class)
    防止多线程同时进入造成 instance 被多次实例化。

    • 饿汉式 静态常量
    //饿汉式(静态常亮)在类装载的时候完成了实例化,避免了线程同步问题。但是没有达到懒加载,有可能造成内存的浪费。
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private final static Singleton instance = new Singleton();
    
        //获取实例
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    • 饿汉式 静态代码块
    //优缺点同上
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private static Singleton instance;
    
        static {
            instance = new Singleton();
        }
    
        //获取实例
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    • 懒汉式 线程不安全
    
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private static Singleton instance;
    
        //获取实例
        public static Singleton getInstance() {
            //在使用的时候才去创建(此处会出现线程不安全问题)
            if (instance == null)
                instance = new Singleton();
            return instance;
        }
    }
    
    • 懒汉式 线程安全 同步方法
    
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private static Singleton instance;
    
        //获取实例    synchronized让线程依次获取instance
        public static synchronized Singleton getInstance() {
            if (instance == null)
                instance = new Singleton();
            return instance;
        }
    }
    
    • 懒汉式 线程安全 同步方法
    //虽然可以解决线程不安全问题,但是每次获取instance都走同步方法,会导致效率低
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private static Singleton instance;
    
        //获取实例    synchronized让线程依次获取instance
        public static synchronized Singleton getInstance() {
            if (instance == null)
                instance = new Singleton();
            return instance;
        }
    }
    
    • 懒汉式 线程安全 同步代码块(双重判断) volatile修饰变量
    //volatile 共享变量 可以在修改后立刻刷新
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //内部创建对象实例
        private static volatile Singleton instance;
    
        //获取实例    synchronized让线程依次获取instance
        public static synchronized Singleton getInstance() {
            if (instance == null)
                //没有作用 因为已经进入if语句了
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            return instance;
        }
    }
    
    • 静态内部类 (外部类被装载的时候,内部类不会被装载)
    
    class Singleton {
        //构造私有化
        private Singleton() {
    
        }
    
        //静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次
        private static class SingletonInstance {
            private static final Singleton instance = new Singleton();
        }
    
        //获取实例    synchronized让线程依次获取instance
        public static synchronized Singleton getInstance() {
            return SingletonInstance.instance;
        }
    }
    

    -枚举方式 避免多线程同步问题,防止反序列化重新创建对象

    enum Singleton {
        INSTANCE;
    
        public void method() {
            System.out.println("单例调用");
        }
    }
    
    单例模式在jdk中的应用

    Runtime对象,用到了饿汉式的单例

    如何破坏单例,如何防止
    • 通过反射破坏单例
      虽然构造方法以及被私有化,但还是可以通过反射产生新的对象。

            //通过反射获取
            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton reflex = constructor.newInstance();
            System.out.println("reflex的hashCode:"+reflex.hashCode());
      

      如何防止反射破坏
      创建一个全局变量设为true,在构造方法中判断,如果是一个次创建,就把变量设为false,如果不是,就抛出异常。

        class Singleton {
          private static boolean isFristCreate = true;//默认是第一次创建
        
          //构造私有化
          private Singleton() {
            if (isFristCreate) {
              synchronized (Singleton.class) {
                if (isFristCreate) {
                  isFristCreate = false;
                }
              }
            } else {
              throw new RuntimeException("已然被实例化一次,不能在实例化");
            }
          }
        
          //静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次
          private static class SingletonInstance {
            private static final Singleton instance = new Singleton();
          }
        
          //获取实例    synchronized让线程依次获取instance
          public static synchronized Singleton getInstance() {
            return SingletonInstance.instance;
          }
        }
      
    • 通过克隆方式破坏

    • 通过反序列化方式破坏

  • 相关阅读:
    golang 带参数 发送、上传本地文件到其他机器、服务器
    【比赛游记】北大集训2020垫底记
    【比赛游记】NOIP2020又当工具人记
    AtCoder Regular Contest 107
    AtCoder Regular Contest 108
    【比赛游记】CSP2020游记
    注意事项
    2020北大集训摸鱼记
    NOIP2020游记
    ARC109F
  • 原文地址:https://www.cnblogs.com/super-hu/p/14246348.html
Copyright © 2011-2022 走看看