设计模式之单例模式
0 概述
- 单例模式是一种常见的设计模式,其目的是确保类只有一个实例,并提供一个全局访问点,这意味着无论何时何地,每次请求该类的实例时,都将获得相同的唯一实例,可帮助我们管理全局状态、资源共享和限制实例化。然而,在使用时需谨慎考虑其适用性,以避免引入不必要的复杂性。
1 使用场景
- 资源共享:当需要在整个应用程序中共享资源(如数据库连接池或日志记录器)时,使用单例模式可以确保所有部分使用的是相同的实例。
- 控制对特定资源的访问:在某些情况下,需要限制对特定资源的访问,以确保整个系统中只存在一个实例。
- 全局状态管理:适合在需要维护全局状态信息的情况下,例如跟踪用户登录状态等。
2 优缺点
- 优点:
- 全局访问点:易于访问单例实例,避免了传统的对象创建方式中的重复实例化。
- 懒加载:可以延迟实例化,节省系统资源。
- 避免竞态条件:可以避免多线程环境下的竞态条件问题。
- 缺点:
- 可能引入全局状态:过度使用单例模式可能导致全局状态的增加,使得代码难以维护和调试。
- 隐藏依赖关系:单例模式会隐藏类之间的依赖关系,可能导致难以进行单元测试。
- 不适合动态扩展:因为单例类的实例化由其自身控制,所以动态更改实例化行为较为困难
3 饿汉式
- 好处:类加载到内存后,JVM 保证了线程安全(只会加载到内存一次)
- 坏处:不管用到与否,类加载的时候就完成了实例化
public class SingletonDemo{ public static final SingletonDemo INSTANCE = new SingletonDemo(); // 私有化的空参构造(保证除了本类,其他类无法 new 这个类的对象) private SingletonDemo(){ } public static SingletonDemo getInstance(){ return INSTANCE; } }
4 懒汉式
-
加锁,但是效率比较低
public class SingletonDemo{ public static SingletonDemo INSTANCE; // 私有化的空参构造(保证除了本类,其他类无法 new 这个类的对象) private SingletonDemo(){ } public static synchronized SingletonDemo getInstance(){ if(INSTANCE == null){ INSTANCE = new SingletonDemo(); } return INSTANCE; } }
-
优化:双重校验
public class SingletonDemo{ // 要加上 volatile 禁止指令重排,否则会有问题 public static volatile SingletonDemo INSTANCE; // 私有化的空参构造(保证除了本类,其他类无法 new 这个类的对象) private SingletonDemo(){ } public static SingletonDemo getInstance(){ if(INSTANCE == null){ // 双重检查 synchronized(SingletonDemo.class){ if(INSTANCE == null){ INSTANCE = new SingletonDemo(); } } } return INSTANCE; } }
-
进一步优化,改为静态内部类的方式(推荐)
- 注意:只加载 SingletonDemo 的话,SingletonDemoHolder 是不会被加载的(只有当调了 getInstance() 方法的时候静态内部类才会被加载)
- 线程安全也是 JVM 保证的
- 也保证了懒加载
public class SingletonDemo{ // 私有化的空参构造(保证除了本类,其他类无法 new 这个类的对象) private SingletonDemo(){ } private static class SingletonDemoHolder{ private static final INSTANCE = new SingletonDemo(); } public static SingletonDemo getInstance(){ return SingletonDemoHolder.INSTANCE; } }
-
终极优化:枚举类 -->《JavaEffective》里的推荐写法
public enum SingletonDemo{ INSTANCE; public SingletonDemo getInstance(){ return INSTANCE; } }
使用枚举类的原因:
- 线程安全性:枚举类确保在 Java 中实现单例是线程安全的,因为枚举类的实例由 JVM 在加载枚举类时自动实例化,而且在任何情况下都只会有一个实例。
- 防止反射攻击:枚举类不仅具有线程安全性,还可以防止通过反射机制进行多次实例化。由于枚举类的构造方法默认是私有的,并且只会被调用一次,因此无法通过反射方式重新创建实例。
- 序列化与反序列化的安全性:枚举类天生支持序列化和反序列化。在序列化和反序列化的过程中,枚举类保证只会有一个实例存在。
-
以上就是单例模式的全部内容了,后续再介绍策略模式
-
创作不易,感谢阅读,若遇到问题,可以关注此微信gzh:EzCoding 留言反馈,希望能够帮助到您