Hello,大家好!我是你们的老朋友小米,今天又来给大家分享技术啦!这次我们来聊一聊在开发中经常会遇到的设计模式——单例模式。
单例模式是设计模式中的一种,它的主要作用是确保某个类在内存中只有一个实例存在。想象一下,假如我们正在设计一个系统,某些对象比如数据库连接或者日志系统需要共享同一个资源,那么单例模式就是理想的选择!
什么是单例模式?单例模式(Singleton Pattern)的核心思想就是“一个类只有一个实例,并且自行向整个系统提供这个实例。”这个实例一般是通过该类自己创建的。
单例模式的特点:
确保某个类只有一个实例。
提供一个全局访问点来访问这个实例。
使用场景:
需要频繁实例化和销毁的对象。比如:多线程的线程池、数据库连接池。
耗费资源过多的对象。比如:文件管理器、日志处理器。
工具类对象。比如:配置文件读取类、全局配置管理类。
全局状态类。比如:系统中状态信息的管理类。
单例模式的实现方式Java 中实现单例模式有几种经典方式,接下来我会依次给大家讲解,并且手写这些实现。Let's Go!
饿汉式(线程安全)这种方式是最简单的一种单例实现方式。它的特点是实例在类加载的时候就被创建好,不管你是否需要它,类加载的时候它就已经在内存中准备好了。
饿汉式分析:
优点:简单明了,线程安全。
缺点:类加载的时候就创建实例,如果该实例长期未被使用,会造成资源浪费。
懒汉式(非线程安全)懒汉式的单例模式,只有在我们调用getInstance()方法的时候,实例才会被创建。如果从效率角度考虑,这样的方式会延迟实例的创建,节省内存。
懒汉式分析:
优点:实例在需要时才创建,节省资源。
缺点:非线程安全。如果多个线程同时调用getInstance()可能会创建多个实例。
懒汉式(线程安全)为了避免懒汉式的线程不安全问题,我们可以使用同步锁来保证线程安全。
线程安全的懒汉式分析:
优点:线程安全,实例在需要时才创建,节省资源。
缺点:使用synchronized关键字会降低性能,尤其是在多线程环境中,频繁的同步会带来性能开销。
双重检查锁定(Double Check Locking)为了提高性能,我们可以对懒汉式的线程安全单例模式进行优化,采用双重检查锁定机制。这样就能在减少同步开销的同时,确保线程安全。
双重检查锁定分析:
优点:延迟初始化、线程安全、效率高。
缺点:实现复杂,容易出错,尤其是在处理细节时容易忽视某些问题。
静态内部类(推荐)这种方式是单例模式中比较推荐的一种实现方式,利用了静态内部类来实现延迟加载,同时保证线程安全。
静态内部类分析:
优点:延迟加载,线程安全,且不需要额外的同步措施,性能较高。
缺点:实现相对复杂,需要对静态内部类机制有一定的了解。
枚举(线程安全,防止反序列化创建新的实例)
Java 枚举类型是天生单例的,并且是线程安全的。枚举方式实现单例不仅简洁,还能防止通过反射和序列化来破坏单例模式。
枚举单例分析:
优点:线程安全,防止反射攻击,防止反序列化。
缺点:枚举的语义有点偏离常规的单例模式,可能会对代码的可读性造成一些影响。
单例模式常见问题
1. 反射破坏单例
通过反射机制可以强行调用私有构造器,破坏单例模式。
解决方法:
在构造方法中添加防御性代码,防止通过反射创建多个实例。
2. 序列化破坏单例
通过序列化机制也能破坏单例模式。
解决方法:
实现readResolve()方法来防止序列化创建新实例。
END单例模式作为设计模式中的基础成员,在开发中非常有用。不同的实现方式适应不同的场景,我们需要根据项目的实际情况来选择最合适的实现。饿汉式简单但会浪费资源,懒汉式延迟加载但有线程安全问题,而静态内部类和枚举方式则提供了更优的解决方案。
希望这篇文章能帮助大家更好地理解单例模式,也欢迎大家在评论区分享你们的见解和疑问!我们下次再见~