分享

你敢说自己了解单例模式?

 股海无涯苦作舟 2021-11-11

一、背景

  最近在学习设计模式,在看到单例模式的时候,我一开始以为直接很了解单例模式了,实现起来也很简单,但是实际上单例模式有着好几个变种,并且多线程中涉及到线程安全问题,那么本文我们就来好好聊聊单例模式,说一下经典三种实现方式:饿汉式、懒汉式、登记式。并且解决掉多线程中可能出现的线程安全问题。

二、基本概念

1.为什么要使用单例模式?

  在我们日常的工作中,很多对象通常占用非常重要的系统资源,比如:IO处理,数据库操作等,那我们必须要限制这些对象只有且始终使用一个公用的实例,即单例。

2.单例模式的实现方式

  • 构造函数私有化,防止其他类生成唯一公用实例外的实例。且

  • 单例类应该被定义为final,也就是说单例类不能被继承,因为如果允许继承那子类就都可以创建实例,违背了类唯一实例的初衷。

  • 类中一个静态变量来保存单实例的引用。

  • 一个共有的静态方法来获取单实例的引用。

3.单例模式的UML类图

图片

4.单例模式的经典实现方式

  • 饿汉式:一开始就创建好实例,每次调用直接返回,经典的“拿空间换时间”。

  • 懒汉式:延迟加载,第一次调用的时候才加载,然后返回,以后的每次的调用就直接返回。经典“拿时间换空间”,多线程环境下要注意解决线程安全的问题。

  • 登记式:对一组单例模式进行的维护,主要是在数量上的扩展,通过线程安全的map把单例存进去,这样在调用时,先判断该单例是否已经创建,是的话直接返回,不是的话创建一个登记到map中,再返回。

三、饿汉式---代码实现

1.单例类

图片

2.测试类

图片

3.测试结果

图片

四、懒汉式---代码实现

1.单例类

图片

2.测试类

图片

3.测试结果

图片

细心的同学已经发现,这种实现方式,在多线程的环境中,是有线程安全安全问题的,有可能两个或多个线程判断instance都为null,然后创建了好几遍实例,不符合单例的思想,我们可以对它进行改进。

五、改进懒汉式1---代码实现

原理:使用JDK的synchronized同步代码块来解决懒汉式线程安全问题

1.单例类

图片

2.测试结果

图片

六、改进懒汉式2---代码实现

原理:使用JVM隐含的同步和类级内部类来解决,JVM隐含的同步解决了多线程情况下线程安全的问题,类级内部类解决只有使用的时候才加载(延迟加载)的问题。

1.JVM隐含的同步有哪些?

  • 静态初始化器(在静态字段上或static{}静态代码块的初始化器)初始化数据时

  • 访问final字段时

  • 在创建线程之前创建对象时

  • 线程可以看见它将要处理的对象时

2.什么是类级内部类?

  • 有static修饰的成员式内部类。没有static修饰的成员式内部类叫对象级内部类。

  • 类级内部类相当于其外部类的static成分,他的对象与外部类对象间不存在依赖关系,因此可直接创建,而对象级内部类的实例,是绑定在外部对象实例中的。

  • 类级内部类中,可以定义静态的方法。在静态的方法中只能够引用外部类的中的静态成员方法或者成员变量

  • 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载

3.单例类

图片

4.测试类

图片

5.测试结果

图片

七、登记式--代码实现

1.基类

图片

2.子类1

图片

3.子类2

图片

4.测试类

图片

5.测试结果

图片

该解决方案的缺点:基类的构造函数对子类公开了(protected),有好的解决方案的博友可以讨论指教~

八、总结

  经过本文,我们就搞明白了什么叫单例模式,如何优雅的实现经典的单例模式,如何进行拓展和开发具有线程安全的单例模式。对于我们以后的开发非常有帮助,也让我们更加了解单例模式。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多