• 设计模式之单例模式


    单例模式

    1.单例模式概述

    基本定义

    单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例

    对于系统的某些类来说,只含有一个实例很重要,有助于我们协调系统的整体行为。比如线程池、缓存、日志对象等等都会被设计为单例,设计的初衷也是为了避免不一致状态。

    2.常见的单例模式

    饿汉式单例

    1. 提供一个私有的默认构造函数避免类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效)
    2. 在类加载初始化时期就创建了静态对象给外部使用,此对象是不会变的,所以本身是线程安全的。
    3. 类加载就完成实例化,没有达到懒加载效果,若不使用会造成内存浪费。
    public class Singleton {
        private Singleton(){}
        private final static Singleton SINGLETON = new Singleton();
        public static Singleton getInstance(){
            return SINGLETON;
        }
    }
    

    懒汉式单例

    1. 线程不安全,单线程环境下可以使用。
    2. 延迟加载对象。
    3. 多线程环境下可能会产生多个Singleton对象
    public class Singleton {
        private static Singleton singleton;
        private Singleton() {}
        public static Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    

    懒汉式单例(同步方法解决线程安全问题)

    1. synchronized同步方法,此方式线程安全。
    2. 效率十分低下。每个线程获取实例,都需要进行等待方法锁释放。
     public class Singleton {
          private static Singleton singleton;
          private Singleton() {}
          public static synchronized Singleton getInstance() {
              if (singleton == null) {
                  singleton = new Singleton();
              }
              return singleton;
          }
      }
    

    懒汉式单例(同步实例-线程不安全写法)

    1. 原本觉得同步整个方法的效率会比较低,假设创建实例中还存在其他的一些操作。所以改成同步创建实例那一步。
    2. 在if(singleton == null)这一步仍然会产生线程安全问题。
     private static Singleton singleton;
          private Singleton() {}
          public static Singleton getInstance() {
              if (singleton == null) {
                  synchronized (Singleton.class) {
                      singleton = new Singleton();
                  }
              }
              return singleton;
          }
    

    懒汉式单例(双重检查----推荐使用)

    1. 上面的同步创建实例的思想是OK的,但是写法上却走向了线程不安全。
    2. 通过两次if(singleton == null)的比较,保证线程安全,且同步实例化代码只需要执行一次,后面再次访问,都不会再进入同步代码块,直接到达下面的return。
    3. 此写法线程安全,且懒加载.
    public class Singleton {
        /*volatile 关键字确保当uniqueInstance变量被初始化成Singleton实例时,
        多个线程正确地处理uniqueInstance
        */
          private static volatile Singleton singleton;
          private Singleton() {}
          public static Singleton getInstance() {
              if (singleton == null) {
                  synchronized (Singleton.class) {
                      if (singleton == null) {
                          singleton = new Singleton();
                      }
                  }
              }
              return singleton;
          }
      }
    

    懒加载单例(静态内部类实现----推荐使用)

    1. 线程安全,懒加载,效率高。
    2. 这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
    3. 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
    public class Singleton {
        private  static  boolean initialized =false;
        /*
        要想使用内部类,必然需要先初始化内部类,
        如果不使用,则不会加载内部类。
        * */
        //构造方法这一段是假如反射攻击的时候能抛出异常。
        private Singleton(){
            synchronized (Singleton.class){
                if (initialized==false){
                    initialized=initialized;
                }else{
                    throw new RuntimeException("单例已经被侵犯");
                }
            }
        }
        //当调用这个方法的时候将会加载内部类
        //static 为了使得单例空间可以共享
        //final  保证这个方法不会被重写重载
        public  static  Singleton getInstance(){
            //在访问这个结果以前,一定会先加载下面的内部类
            return LazyHolder.LAZY_THREE;
        }
        //默认不加载
        private static class LazyHolder{
            private static  final LazyThree LAZY_THREE =new Singleton();
        }
    }
    

    懒加载单例(枚举方式----推荐使用)

    借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。

    public enum Singleton {
        INSTANCE;
        public void whateverMethod() {
        }
    }
    

    3.spring中单例模式的实现:注册式

    //Spring 中的单例实现  注册式实现
    public class BeanFactory {
    	/*
    	注册式单例维护的实际是一组单例类,若已经注册过,那么从IOC容器中直接返回,
    	若没有注册过,先注册,再返回。
    	*/
        public static Map<String,Object> ioc=new ConcurrentHashMap<>();
    
        public  static  Object getBean(String className){
            if(ioc.containsKey(className)){
                return  ioc.get(className);
            }else{
                Class clazz=null;
                Object obj= null;
                try {
                    obj = Class.forName(className).newInstance();
                    return ioc.put(className,obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            }
        }
    }
    

    4.解决反序列化重新创建对象的问题

    public class Seriable implements Serializable {
        public  final  static  Seriable  INSTANCE =new Seriable();
        private  Seriable(){}
        public static  Seriable getInstance(){
            return  INSTANCE;
        }
        /*在反序列化的时候会重新创建对象 (new )
        * 但是有的对象是单例的呢,你不能创建多个对象吧
        * 所以通过下面这个方式可以控制只生成一个对象。
        * */
        private  Object readResolve(){
            return INSTANCE;
        }
    }
    

    5.单例模式为什么必须是静态的

    先搞清单例模式的实现过程:

    1. 先将该类的构造函数私有化(目的是禁止其他程序创建该类的对象);
    2. 其次,在本类中自定义一个对象(既然禁止其他程序创建该类的对象,就要自己创建一个供程序使用,否则类就没法用,更不是单例);
    3. 最后,提供一个可访问类自定义对象的类成员方法(对外提供该对象的访问方式);

    核心就是你只能用我自己创建的对象。

    程序调用类的方式:

    1. 创建类的一个对象,用该对象去调用类中方法
    2. 使用类名直接调用类中方法,格式“类名.方法名()”

    上面说了,构造函数私有化后第一种情况就不能用,只能使用第二种方法。
    而使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此类自定义的实例变量也必须是静态的。这就是单例模式为什么必须是静态的原因。


    谢谢以下前辈

    参考:https://www.cnblogs.com/zhaoyan001/p/6365064.html

    参考:https://zhidao.baidu.com/question/2206072272164938188.html


  • 相关阅读:
    linux shell 总结
    python小结
    python与execl的读写
    利用burpsuits暴力破解登陆界面
    python之函数的使用
    Sublime text怎么识别input函数
    ping的禁止
    Hbase的配置与使用
    JAVA 利用反射自定义数据层框架
    JAVA实现网页上传头像
  • 原文地址:https://www.cnblogs.com/1314xf/p/10129537.html
Copyright © 2020-2023  润新知