设计模式之单例模式

设计模式之单例模式

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;
        }
    }
    

    使用枚举类的原因:

    1. 线程安全性:枚举类确保在 Java 中实现单例是线程安全的,因为枚举类的实例由 JVM 在加载枚举类时自动实例化,而且在任何情况下都只会有一个实例。
    2. 防止反射攻击:枚举类不仅具有线程安全性,还可以防止通过反射机制进行多次实例化。由于枚举类的构造方法默认是私有的,并且只会被调用一次,因此无法通过反射方式重新创建实例。
    3. 序列化与反序列化的安全性:枚举类天生支持序列化和反序列化。在序列化和反序列化的过程中,枚举类保证只会有一个实例存在。
  • 以上就是单例模式的全部内容了,后续再介绍策略模式

  • 创作不易,感谢阅读,若遇到问题,可以关注此微信gzh:EzCoding 留言反馈,希望能够帮助到您

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值