• 单例设计模式


    单例设计模式

    知识点

    • 1.模式定义/应用场景/类图分析

    • 2.字节码知识/字节码指令重排序

    • 3.类加载机制

    • 4.JVM序列化机制

    • 5.单机模式在Spring框架 & JDK源码中的应用

    懒加载模式

    延迟加载,只有在真正使用的时候,才开始实列化。

    • 线程安全问题

    • double check 加锁优化

    • 编译器(JIT) CPU有可能对指令进行重排序,导致使用道尚未初始化的实列,可以通过添加volatile关键字进行修饰,对于volatile修饰的段,可以防止指令重排。


    什么是指令重排,这里我们用javap -v xxx.class查看new 一个对象的过程。

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

    饿汉模式

    基于JVM的类加载模,保证实例的唯一性

    类的加载过程:

    • 1.加载二进制数据道内存中,生成对应的Class数据结构

    • 2.连接: a.验证,b-准备(给类的静态成员变量赋默认值),c-解析

    • 3.初始化:给类的静态变量赋初值


    只有在真正使用对应的类时,才会触发初始化 如直接进行new操作,访问静态属性,访问静态方法,用反射访问类,初始化一个类的子类等

    
    class HungrySingleton {
    
      private static HungrySingleton instance = new HungrySingleton();
    
      private HungrySingleton() { }
    
      public static HungrySingleton getInstance() {
    
    ​    return instance;
    
      }
    
    }
    
    

    内部类加载模式

    
    class InnerSingletonClass {
    
      private static class InnerSingletonHolderClass {
    
    ​    private static InnerSingletonClass instance = new InnerSingletonClass();
    
      }
    
      private InnerSingletonClass() {
    
      }
    
      public static InnerSingletonClass getInstance() {
    
    ​    return InnerSingletonHolderClass.instance;
    
      }
    
    }
    
    

    调用getInstance()方法时,才调用这个内部类。本质上也是一种懒加载的一种形式

    反射调用

    在以上三种中,不做任何措施,时没有做到单例的用途。以懒加载模式为例如:

    
     Constructor<LazySingletonClass> declaredConstructors = LazySingletonClass.class.getDeclaredConstructor();
    
    ​    declaredConstructors.setAccessible(true);
    
    ​    LazySingletonClass lazySingletonClass = declaredConstructors.newInstance();
    
    ​    InnerSingletonClass instance = InnerSingletonClass.getInstance();
    
    ​    System.out.println(lazySingletonClass);
    
    ​    System.out.println(instance);
    
    

    结果为:

    
    org.example.singleton.LazySingletonClass@1540e19d
    
    org.example.singleton.InnerSingletonClass@677327b6
    
    

    明显不是同一个实列。在以类的静态实列一起加载的模式,如内部类,在初始化时,即可以在私有的构造方法中加入一个判断。

    
    private InnerSingletonClass() {
    
    ​    if(InnerSingletonHolderClass.instance != null){
    
    ​      throw new RuntimeException("单例不允许多个实例");
    
    ​    }
    
      }
    
    

    单例模式的应用

    Runtime

    饿汉模式

    
    public class Runtime {
    
    ​    private static Runtime currentRuntime = new Runtime();
    
    ​    /**
    
    ​     * Returns the runtime object associated with the current Java application.
    
    ​     * Most of the methods of class <code>Runtime</code> are instance
    
    ​     * methods and must be invoked with respect to the current runtime object.
    
    ​     *
    
    ​     * @return  the <code>Runtime</code> object associated with the current
    
    ​     *          Java application.
    
    ​     */
    
    ​    public static Runtime getRuntime() {
    
    ​        return currentRuntime;
    
    ​    }
    
    
    
    ​    /** Don't let anyone else instantiate this class */
    
    ​    private Runtime() {}
    
    }
    
    

    Currency

    序列化,的处理方式,它也是提供了readResolve方法。

    
      /**
    
    ​     * Resolves instances being deserialized to a single instance per currency.
    
    ​     */
    
    ​    private Object readResolve() {
    
    ​        return getInstance(currencyCode);
    
    ​    }
    
    

    DefaultSingletonBeanRegistry

    ReactiveAdapterRegistry

    
    public static ReactiveAdapterRegistry getSharedInstance() {
    
    ​	ReactiveAdapterRegistry registry = sharedInstance;
    
    ​	if (registry == null) {
    
    ​		synchronized (ReactiveAdapterRegistry.class) {
    
    ​			registry = sharedInstance;
    
    ​			if (registry == null) {
    
    ​				registry = new ReactiveAdapterRegistry();
    
    ​				sharedInstance = registry;
    
    ​			}
    
    ​		}
    
    ​	}
    
    ​	return registry;
    
    }
    
    

    ProxyFactoryBean

    弯弯月亮,只为美好的自己。
  • 相关阅读:
    expect脚本实例
    Linux dialog详解(图形化shell)
    makefile——小试牛刀
    gdb入门
    linux常见系统调用函数列表
    linux前后台任务的切换以及执行暂停
    centos 7.0 lnmp安装部署步骤
    环境列表
    setjmp与longjmp非局部跳转函数的使用
    malloc,calloc,alloca和free函数
  • 原文地址:https://www.cnblogs.com/Choleen/p/15037505.html
Copyright © 2020-2023  润新知