工作中常用到的设计模式-单例模式

工作中常用到的设计模式-单例模式

一、单例模式

业务场景:单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。I/O与数据库的连接,一般就用单例模式实现de的。

哪些情况可能会出现单例被破坏?

  • 多线程破坏单例
    • 多个线程同时操作,导致同时创建多个对象
    • 解决:DCL双重检查锁写法;用静态内部类的写法;枚举式
  • 指令重排破坏单例
    • 1、分配内存instance = memory ;2、赋值引用 ;3、初始化对象
    • 解决:volatile 修饰
  • 克隆破坏单例
    • 深clone(),每次都会重新创建新的实例
    • 解决:在单例对象中重写clone() 方法
  • 反序列化破坏单例
    • 反序列化对象会重新分配内存,相当于重新创建对象
    • 解决: 需要重写readResolve()方法,将返回值设置为单例对象
  • 反射破坏单例
    • 反射可以任意调用私有构造方法创建单例对象
    • 解决:在构造方法中检查单例对象,如果已创建则抛出异常;枚举式单例

总结:如果经常发生多线程并发情况,推荐使用静态内部类和枚举式单例

1.1、饿汉式

也是预加载技术,系统启动后,单例对象就已经创建完毕,无论是否以后会被使用。源码示例如下:
在这里插入图片描述

注意构造函数,是private修饰的。这样做的一个很重要的目的,就是防止使用new关键字来创建一个新的实例。

1.2、懒汉式

懒汉式单例在类加载时不进行实例化,而是使用者第一次调用getInstance方法时进行实例化。这种延迟加载技术可以提高系统启动速度
在这里插入图片描述
注意为了防止多个线程同时调用getInstance方法,需要在该方法前面增加关键字synchronized进行线程访问锁定。在高并发场景下,会对系统性能产生较大的影响。

1.3、双重检查锁定式

针对以上懒汉式存在的问题,解决方法就是降低锁定的粒度,只锁定部分代码,双重检查锁定(Double-Check Locking)。volatile为了解决指令重排序导致instance半初始化问题

在这里插入图片描述

1.4、静态内部类实现式

这种实现方式,既没有饿汉式单例无论是否使用都要占用内存的问题,也不存在上述懒汉式单例性能问题和线程安全控制复杂的问题
在这里插入图片描述

注意static关键字表示的是类级内部类,类级内部类只有在使用时才会被加载,而且静态变量的初始化是由JVM保证线程安全的。

总结一下,当getInstance方法第一次被调用的时候,它第一次读取InstanceHolder.instance,导致InstanceHolder类得到初始化。而InstanceHolder类在装载并被初始化的时候,会初始化它的静态域,从而创建HolderSingleton的实例。

优点就是getInstance方法并没有做线程同步控制,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

1.5、枚举式
在这里插入图片描述

总结一下,首先创建Enum时,编译器会自动为我们生成一个继承自java.lang.Enum的类,枚举成员声明中被static和final所修饰,根据在静态内部类式单例中讲过的,虚拟机会保证这个成员在多线程环境中被正确的加锁和同步,所以是线程安全的。另外,Enum的构造方法本身就是private限制的,所以也防止了使用new关键字创建新实例。

1.6、单例注册表式

Spring框架中的单例模式使用
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值