首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小司机带你学习单例模式的六种姿势!

小司机带你学习单例模式的六种姿势!

作者头像
Wizey
发布2018-10-10 10:37:10
4840
发布2018-10-10 10:37:10
举报
文章被收录于专栏:编程心路编程心路

单例模式是创建型模式的一种,下面总结一下在 Java 中实现单例模式的几种方法,并在多线程环境中进行了测试。

一、单例模式概念

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供了全局访问的方法。

单例模式的三个特点:

  • 这个类只能有一个实例。
  • 这个类自行创建这个实例。
  • 这个类自行向整个系统提供这个实例。

单例模式的应用有,Windows 里的任务管理器(一个系统中只有一个)、网站计数器(实现多个页面的计数同步)、数据库连接池(减少资源损耗)等。

二、单例模式实现

根据单例模式的特点,我们来实现单例模式,在类中提供一个静态方法来获取这个唯一的实例对象,给其他类提供实例,并且这个实例对象不能直接使用 new 创建,所以构造方法要声明成私有,这便是最简单的单例模式实现。这样的实现在单线程环境中,当然没问题,但是我们还要考虑多线程环境下的安全实现。

下面是对单例模式的各种实现,并且对每种实现方法都在多线程环境中做了测试,所有代码都在我的 GitHub 仓库中中,传送门。该仓库还在完成中,用 Java 实现 23 种设计模式,并对设计原则和每种设计模式做出详细的分析,感兴趣的可以 fork 或者 star 哦,也欢迎小伙伴参与该仓库的完成。

2.1 懒汉式单例类(线程不安全)

通过 getInstance() 方法得到单例对象,单例对象在需要的时候才被延迟创建,所以称之为懒汉式。但是在多线程环境中,由于这个 getInstance() 方法可能被多个线程同时调用,这很可能会创建多个实例,所以这种实现在多线程环境下是不安全的。

懒汉式-线程不安全.jpg

2.2 懒汉式单例类(线程安全)

给 getInstance() 加上 synchronized 关键字后,可以保证这个方法在同一时间只能被一个线程调用,多个线程调用这个方法要排队依次调用,这就保证了只会创建一个单例对象,在多线程环境下是安全的。

懒汉式-线程安全.jpg

2.3 饿汉式单例类

相比于上面的懒汉式,饿汉式在类加载的时候就会创建实例对象,在 getInstance() 方法直接返回创建好的对象,简单直接,在多线程环境下也是安全的。

饿汉式.jpg

2.4 双重校验锁单例类

针对于上面的线程安全的懒汉式加载,这种实现方式不是直接给方法加上 synchronized 关键字,而是在 getInstance() 方法做双重检查来解决线程不安全的问题。这种方式允许多个线程同时调用该方法,但是在方法中会进行两次检查,第一次检查实例是否已经存在,如果不存在才进入下面的同步代码块,线程安全的创建实例,如果实例真的不存在(避免这是有其他线程创建好了,再次创建新的实例)才会创建实例。这种方式理论上要比直接使用 synchronized 关键字性能要高,但是对于不同虚拟机对 volatile 关键字的优化,优势并不明显。

双重校验式.jpg

2.5 静态内部类单例类

创建一个静态内部类,来创建实例,和上面饿汉式相比,虽然都是直接 new 实例,但是这种方式在外部类加载时,静态内部类并不会被加载。只有在第一次调用 getInstance() 方法时,才会显式的加载静态内部类,创建实例,也是一种延迟(懒)创建方式。

静态内部类.jpg

2.6 枚举单例类

枚举实现单例模式是 Java 大牛们比较推荐的,因为这种方式实现非常简单,并且这种方式但是大多数单例模式的实现并不是这种方式。这种方式需要开发者对枚举有清晰的认识,这里也简单的回顾一下枚举的基本知识。

枚举是在 Java1.5 之后出现的,可以更加简单的定义常量,通过反编译,我们可以发现枚举其实也是一个 Java 类,这个类继承自 Enum 接口,定义的枚举对象会被加上 static final 关键字,这就是我们不用枚举时声明常量的方式,另外在 static 静态代码块中初始化枚举对象,枚举的构造方法被加上了 private 关键字,防止其他类创建新的枚举对象实例。虽然前面几种方式无法直接使用 new 创建新的实例,但是可以用反射来绕过 private 限制,而枚举却有自带的序列化机制、防止反射攻击造成多次实例化、线程安全的优点,从这些地方我们都可以看出使用枚举是实现单例模式的绝佳方式。

枚举式.jpg

三、单例模式总结

根据对资源加载时机的需要,来选择合适的单例模式实现方式,如果是懒加载方式,可以选择懒汉方式和双重校验锁方式;如果将资源加载的时间提前来达到使用时的快速体验,可以选择饿汉方式;如果涉及到序列化创建单例对象,可以选择枚举方式。

单例模式的优点是提供了对唯一实例的访问控制,可以节约系统资源,但缺点是单例类的职责过重,并且缺少抽象层难以扩展,不太符合单一职责原则。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.10.02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、单例模式概念
  • 二、单例模式实现
    • 2.1 懒汉式单例类(线程不安全)
      • 2.2 懒汉式单例类(线程安全)
        • 2.3 饿汉式单例类
          • 2.4 双重校验锁单例类
            • 2.5 静态内部类单例类
              • 2.6 枚举单例类
              • 三、单例模式总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档