单例模式(Singleton Pattern)
设计模式是一套被反复使用、多数人知晓、经过分类编目的代码设计经验的总结。使用设计模式是为了保证代码的可靠性,提高工作效率,让代码更容易被他人理解。Java 共有 23种成熟的的设计模式。
使用单例模式改造数据库连接功能
单例模式是一种比较基础的设计模式,应用非常广泛,如应用程序的日志应用,Web 应用的配置文件读取,数据库连接池的设计,网站的计数器等,并且 SpringMVC 框架的 Controller 默认也是单例的。那么到底什么是单例模式?单例模式的优点又有哪些?
单例模式的关键在系统运行期间,某个类有且只有一个实例,这种设计模式的最大优点在于可以对资源进行重复利用,节约重复创建和销毁的成本,从而降低服务器压力,提高程序效率。
定义与目的:
单例模式是一种常用的软件设计模式,其核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。这种模式有助于避免实例化过多的对象,减少系统开销,并提高性能。
优点:
- 节省资源:避免了对资源(如数据库连接、文件句柄等)的多重占用。
- 提高性能:减少了实例化的开销,提高了系统效率。
- 全局访问:提供了一个全局访问点,便于管理。
实现方式:
- 饿汉式:在类加载时就完成了实例的创建,因此线程安全但可能导致资源浪费。
- 懒汉式:在第一次调用
getInstance()
方法时才创建实例,可能存在线程安全问题,但可以通过添加synchronized
关键字解决。 - 双重检查锁定(Double-Checked Locking):既实现了延迟加载,又保证了线程安全,同时避免了
synchronized
带来的性能问题。注意使用volatile
关键字确保多线程环境下的可见性。 - 静态内部类:利用classloader的机制保证初始化instance时只有一个线程,实现了延迟加载和线程安全。
- 枚举式:通过枚举实现单例,既简洁又自动支持序列化机制,防止多次实例化。
下面对两种单例模式做简单对比
懒汉模式:在类加载时暂不创建实例,因此类加载速度快,但是运行时获取对象的速度相对慢,具备延迟加载的特性,但是存在线程安全的问题。
饿汉模式:在类加载时就完成初始化,所以类加载较慢,但获取对象速度快。如果说懒汉模式是"时间换空间",那么饿汉模式就是"空间换时间",因为一开始就创建了实例,所以每次使用时,直接返回该实例即可。
应用场景:
单例模式适用于需要全局唯一实例的场景,如数据库连接池、配置文件读取、日志记录、缓存处理等。
统一异常处理
定义与目的:
统一异常处理是一种将不同类型的异常进行统一处理,以简化代码和提高可读性的技术。它有助于减少重复的异常处理代码,提高代码的可维护性。
实现方式:
- 局部异常处理:在特定的方法或控制器中捕获并处理异常。
- 全局异常处理:
- 在Spring框架中,可以使用
@ControllerAdvice
和@ExceptionHandler
注解实现全局异常处理。 - 在Web.xml中配置全局异常处理器,如
SimpleMappingExceptionResolver
。 - 通过AOP(面向切面编程)进行异常处理。
- 在Spring框架中,可以使用
优点:
- 提高代码可读性:将异常处理逻辑集中管理,减少代码冗余。
- 提高可维护性:修改异常处理逻辑时,只需在统一的地方进行修改。
- 提升用户体验:可以统一处理用户友好的错误页面或消息。
应用场景:
统一异常处理适用于业务逻辑处理、数据库操作、Web服务等可能抛出异常的场景。
总结
在框架升级过程中,单例模式和统一异常处理是提高系统稳定性和可维护性的重要手段。单例模式通过确保类的唯一实例来减少资源占用和提高性能;统一异常处理则通过集中管理异常处理逻辑来简化代码和提高可读性。两者结合使用,可以显著提升系统的整体质量和开发效率。
在实际应用中,需要根据具体场景和需求选择合适的实现方式,并关注线程安全、性能优化等问题,以确保系统的稳定运行和高效开发。
懒汉模式和饿汉模式的总结
懒汉模式和饿汉模式是单例模式的两种常见实现方式,它们的主要区别在于实例化的时机和线程安全性上。
懒汉模式
1. 定义与特点
- 懒汉模式是指全局的单例实例在第一次被使用时构建,即懒加载(Lazy Loading)。在类被加载时不会立即创建实例,而是在首次使用时才会创建。
- 懒汉模式的主要优点是实现了延迟加载,避免了类加载时就创建实例可能导致的资源浪费。
- 然而,懒汉模式在多线程环境下存在线程安全问题,需要额外的同步措施来保证线程安全。
2. 实现方式
- 最简单的懒汉模式实现是直接在
getInstance()
方法中判断实例是否存在,不存在则创建。但这种实现方式在多线程环境下是不安全的。 - 为了解决线程安全问题,可以在
getInstance()
方法上加上synchronized
关键字,但这会降低性能,因为每次调用getInstance()
时都需要获取锁。 - 双重检查锁定(Double-Checked Locking)是一种更高效的线程安全实现方式。它首先检查实例是否存在,如果不存在则进入同步块,再次检查实例是否存在(以避免在同步块中不必要的实例化),如果不存在则创建实例。此外,还需要使用
volatile
关键字来防止指令重排序。 - 静态内部类也是一种线程安全的懒汉模式实现方式。静态内部类在外部类被加载时不会被初始化,只有在被使用时才会被加载和初始化,从而实现了懒加载的效果。同时,由于静态内部类的特性,它也保证了线程安全。
饿汉模式
1. 定义与特点
- 饿汉模式是指全局的单例实例在类装载(ClassLoader)时构建,即类加载时就完成了实例的创建。
- 饿汉模式的主要优点是简单、高效,并且由于实例在类加载时就已经创建,因此在多线程环境下也是安全的(不需要额外的同步措施)。
- 然而,饿汉模式的缺点是可能会导致资源浪费,因为无论是否需要使用该实例,它都会在类加载时被创建。
2. 实现方式
- 饿汉模式的实现通常是在类中直接声明一个私有的静态实例,并提供一个公有的静态方法来获取这个实例。由于实例是静态的,它会在类加载时被初始化。
- 为了防止反射攻击(通过反射机制绕过私有构造函数的限制来创建多个实例),可以在构造函数中添加一些检查逻辑来确保实例的唯一性。但这种方法并不是完全可靠的,因为反射机制可以绕过这些检查。
总结
- 懒汉模式和饿汉模式各有优缺点,选择哪种模式取决于具体的应用场景和需求。
- 如果需要延迟加载实例并且对性能有较高要求,可以选择懒汉模式,但需要注意线程安全问题。
- 如果不需要延迟加载并且希望实现简单、高效且线程安全的单例模式,可以选择饿汉模式。但需要注意可能导致的资源浪费问题。
在实际应用中,可以根据具体需求选择合适的单例模式实现方式,并结合其他设计模式和技术手段来优化系统的性能和稳定性。