Hello,大家好!我是你们的老朋友小米,今天又来给大家分享技术啦!这次我们来聊一聊在开发中经常会遇到的设计模式——单例模式。

单例模式是设计模式中的一种,它的主要作用是确保某个类在内存中只有一个实例存在。想象一下,假如我们正在设计一个系统,某些对象比如数据库连接或者日志系统需要共享同一个资源,那么单例模式就是理想的选择!

什么是单例模式?

单例模式(Singleton Pattern)的核心思想就是“一个类只有一个实例,并且自行向整个系统提供这个实例。”这个实例一般是通过该类自己创建的。

单例模式的特点:

  • 确保某个类只有一个实例。
  • 提供一个全局访问点来访问这个实例。

使用场景:

需要频繁实例化和销毁的对象。比如:多线程的线程池、数据库连接池。

耗费资源过多的对象。比如:文件管理器、日志处理器。

工具类对象。比如:配置文件读取类、全局配置管理类。

全局状态类。比如:系统中状态信息的管理类。

单例模式的实现方式

Java 中实现单例模式有几种经典方式,接下来我会依次给大家讲解,并且手写这些实现。Let's Go!

饿汉式(线程安全)

这种方式是最简单的一种单例实现方式。它的特点是实例在类加载的时候就被创建好,不管你是否需要它,类加载的时候它就已经在内存中准备好了。

手把手教你手写单例,六种实现方式一网打尽!_单例模式

饿汉式分析:

  • 优点: 简单明了,线程安全。
  • 缺点: 类加载的时候就创建实例,如果该实例长期未被使用,会造成资源浪费。

懒汉式(非线程安全)

懒汉式的单例模式,只有在我们调用 getInstance() 方法的时候,实例才会被创建。如果从效率角度考虑,这样的方式会延迟实例的创建,节省内存。

手把手教你手写单例,六种实现方式一网打尽!_单例模式_02

懒汉式分析:

  • 优点: 实例在需要时才创建,节省资源。
  • 缺点: 非线程安全。如果多个线程同时调用 getInstance() 可能会创建多个实例。

懒汉式(线程安全)

为了避免懒汉式的线程不安全问题,我们可以使用同步锁来保证线程安全。

手把手教你手写单例,六种实现方式一网打尽!_单例模式_03

线程安全的懒汉式分析:

  • 优点: 线程安全,实例在需要时才创建,节省资源。
  • 缺点: 使用 synchronized 关键字会降低性能,尤其是在多线程环境中,频繁的同步会带来性能开销。

双重检查锁定(Double Check Locking)

为了提高性能,我们可以对懒汉式的线程安全单例模式进行优化,采用双重检查锁定机制。这样就能在减少同步开销的同时,确保线程安全。

手把手教你手写单例,六种实现方式一网打尽!_单例模式_04

双重检查锁定分析:

  • 优点: 延迟初始化、线程安全、效率高。
  • 缺点: 实现复杂,容易出错,尤其是在处理细节时容易忽视某些问题。

静态内部类(推荐)

这种方式是单例模式中比较推荐的一种实现方式,利用了静态内部类来实现延迟加载,同时保证线程安全。

手把手教你手写单例,六种实现方式一网打尽!_线程安全_05

静态内部类分析:

  • 优点: 延迟加载,线程安全,且不需要额外的同步措施,性能较高。
  • 缺点: 实现相对复杂,需要对静态内部类机制有一定的了解。

枚举(线程安全,防止反序列化创建新的实例)

Java 枚举类型是天生单例的,并且是线程安全的。枚举方式实现单例不仅简洁,还能防止通过反射和序列化来破坏单例模式。

手把手教你手写单例,六种实现方式一网打尽!_内部类_06

枚举单例分析:

  • 优点: 线程安全,防止反射攻击,防止反序列化。
  • 缺点: 枚举的语义有点偏离常规的单例模式,可能会对代码的可读性造成一些影响。

单例模式常见问题

1. 反射破坏单例

通过反射机制可以强行调用私有构造器,破坏单例模式。

解决方法:

在构造方法中添加防御性代码,防止通过反射创建多个实例。

手把手教你手写单例,六种实现方式一网打尽!_内部类_07

2. 序列化破坏单例

通过序列化机制也能破坏单例模式。

解决方法:

实现 readResolve() 方法来防止序列化创建新实例。

手把手教你手写单例,六种实现方式一网打尽!_单例模式_08

END

单例模式作为设计模式中的基础成员,在开发中非常有用。不同的实现方式适应不同的场景,我们需要根据项目的实际情况来选择最合适的实现。饿汉式简单但会浪费资源,懒汉式延迟加载但有线程安全问题,而静态内部类和枚举方式则提供了更优的解决方案。

希望这篇文章能帮助大家更好地理解单例模式,也欢迎大家在评论区分享你们的见解和疑问!我们下次再见~

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号软件求生,获取更多技术干货!