• 单例模式


    单例模式:

      单例模式:(Singleton Pattern, SP)

      确保一个类在任何情况下都绝对只有一个实例,并提供一个全局的访问点

      创建型模式

      应用场景:
        公司CEO
        部门经理

      总结:
      1、私有化构造器
      2、保证线程安全
      3、延迟加载
      4、防止序列化和反序列化的破坏单例
      5、防御反射攻击单例

      源码中的体现:
        ServletContext
        ServletContextConfig

        ApplicationContext
        数据库的连接池

      优点:
        在内存中只有一个实例,减少内存的开销

      缺点:
        没有接口


    ============================================================================

    饿汉式单例模式:

      在类加载的时候就立即初始化,并且创建单例对象

      绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题

      优点:
        没有加任何锁,执行效率比较高,性能高

      缺点:
        类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能“占着茅坑不拉屎”

      案例:
        Spring中IoC容器 ApplicationContext 本身就是典型的饿汉式单例模式

      饿汉式单例模式适用于单例对象较少的情况?
      对象较少的情况指的是(单例数量已知并可控)
      如果单例数量未知,例如spring中的单例,在写框架的时候是不知道用户会搞多少个单例出来的。

      源码:

        pattern.singleton.hungry.HungrySingleton

    ============================================================================

    懒汉式单例模式:

      被外部类调用的时候内部类才会加载。

      基础的懒汉式写法,有一定的概率会出现两种不同的结果,这意味着懒汉式单例存在线程安全隐患

      【如何优化代码,使得懒汉式单例模式在线程环境下安全?】

        1、给getInstance()方法加上 【synchronized】 关键字,解决创建两个实例的问题,但是使得方法变成线程同步方法。
          源码:pattern.singleton.lazy.LazySimpleSingletion

        2、双重检测锁
          优化性能问题,第一次检查是否要阻塞,第二次检查是否要重新创建实例
          volatile 解决指令内存重排序
          源码:pattern.singleton.lazy.LazyDoubleCheckSingleton

        3、静态内部类(兼顾解决饿汉式的内存浪费问题和synchonized的性能问题,但是如果对构造函数不进行处理,存在被反射破环的风险)
          优点:写法优雅,利用java语法特点,性能高

          缺点:能够被反射破坏

          源码:pattern.singleton.lazy.LazyStaticInnerClassSingleton

      优点:节省了内存 线程安全

      缺点:并发请求可能会创建两个实例


    ============================================================================

    反射破化单例:

      对于使用静态内部类方式实现的单例,如果适用反射来调用其构造方法,再调用getInstance()方法,则有两个不同的实例
      源码:ReflectTest
      
    ============================================================================

    序列化破化单例:

      一个单例对象创建好后,有时候需要把对象序列化然后写入磁盘,下次使用时从磁盘读取并进行反序列化,转化未内存对象。但是反序列化的对象会被重新分配内存(即重新创建)。

      如果序列化的对象时单例,就违背了单例模式的初衷,相当于破坏了单例。

      如何保证序列化的情况下也能实现单例模式呢?
        增加一个readResolve()方法
        private Object readResolve(){ return INSTANCE;}
        虽然增加readResolve()方法返回实例解决了单例模式被破坏的问题,但是实际上还是实例了两次(只不过新创建的对象没有被返回而已)

        源码:pattern.singleton.seriable.SeriableSingleton
        SeriableSingletonTest

      反序列化:
      将持久化的字节码内容,通过IO输入流读到内存中来
      转化成一个java对象

    ============================================================================

    注册式单例模式:

      又叫登记式单例模式,就是将每一个实例都登记到某一个地方,使用唯一的标识获取实例。

      注册式单例有两种:枚举式单例、容器式单例

      枚举式单例:
        不会被反射和序列化破化,是推荐的一种单例模式的实现方法

      容器式单例:
        适用于实例非常多的情况,便于管理。但是他是非线程安全的。

    ============================================================================

    线程单例实现 ThreadLocal

      ThreadLocal 不能保证其创建的对象全局唯一,但能保证在单个线程中是唯一的,天生是线程安全的

      单例模式为了达到线程安全的目的,会给方法加上锁,以时间换空间

      ThreadLocal 将所有的对象全部放在 ThreadLocalMap中,为每个线程都提供一个对象,以空间换时间来实现线程隔离的


    ============================================================================

    单例模式总结:

      单例模式可以保证内存中只有一个实例,减少内存的开销,避免对资源的多重占用。

      高频的面试点

    作业:
      怎么解决容器式单例的线程安全?



  • 相关阅读:
    Hibernate 再接触 事务隔离机制
    Hibernate 再接触 一级缓存 二级缓存 查询缓存
    Hibernate 再接触 性能优化
    Hibernate 再接触 HQL
    Hibernate 再接触 树状结构设计以及学生课程成绩表的设计
    DotNetBar.MetroTilePanel 样式、加载数据、获取数据
    C# superGridControl 样式设置、加载数据、获取数据
    system.data.oracleclient 需要 8.17 需要oracle客户端问题
    程序员必备
    LinQ to sql
  • 原文地址:https://www.cnblogs.com/fightingtong/p/12851736.html
Copyright © 2020-2023  润新知