• 设计模式——单例模式8种实现


    单例模式

    1、了解概念:

    单例模式是指采用一定的方法保证在整个软件系统种,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

    个人理解:一个类只能实例化一个对象,其他的使用都是引用这个对象。

    单例模式的八种实现方式

    1、饿汉式
        a、静态常量(推荐)
        b、静态代码块(推荐)
    2、懒汉式
        a、线程不安全
        b、线程安全,同步方法
        c、线程安全,同步代码块
    3、双重检查(推荐)
    4、静态内部类(推荐)
    5、枚举(推荐)

    单例模式的作用:

    1、节省资源:有些对象在程序中只需要一个实例,如果反复创建多个实例会造成资源的浪费,而单例模式可以确保只创建一个实例,节约资源。
    ​
    2、全局访问点:是单例模式中提供给其他代码访问单例对象的接口或方法,它封装了单例对象的创建和访问逻辑,使得其他部分可以方便地获取单例对象,同时可以控制访问权限和简化调用方式。
    ​
    3、保持一致性:单例模式可以确保一个类只有一个实例,从而避免了多个实例之间状态的不一致性。
    ​
    4、延迟初始化:有些对象的初始化比较耗时,使用单例模式可以将对象的初始化延迟到真正需要的时候进行,提高程序的性能。
    ​
    5、线程安全:单例模式可以保证在多线程环境下只有一个实例被创建,避免了多线程并发访问时可能出现的问题。

    饿汉式

    1、静态常量

    步骤:

    1、构造器私有化

    2、类的内部创建对象

    3、向外暴露一个静态公共方法(getInstance)

    4、代码实现

    1. public class Singleton {
    2.   // 创建静态常量实例
    3.   private static final Singleton instance = new Singleton();
    4.    
    5.   // 私有构造函数,禁止外部通过new关键字创建实例
    6.   private Singleton() {
    7.       // 在此处进行初始化操作
    8.   }
    9.    
    10.   // 公共方法,用于获取实例
    11.   public static Singleton getInstance() {
    12.       return instance;
    13.   }
    14. }
     
    

    优点:

    • 写法简单,线程安全、效率高,在类装载的时候完成实例化,避免了线程同步问题

    缺点:

    • 内存浪费:在类加载的时候完成了实例化,

    • 无法实现懒加载:没有达到Lazy Loading的效果

    • 不支持传参

    说明:

    • 这种方式基于classloder机制避免了多线程同步问题,但是instance在类装载时就实例化,单例模式中大多数是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他静态方法导致类装载,这个时候初始化instance就没有导致lazy loading的效果

    结论:

    • 这种单例模式可用,但是可能会造成浪费内存

    补充:lazy loading

    Lazy loading(延迟加载)是一种设计模式,它指的是在需要的时候才加载数据或资源,而不是在初始化时就立即加载。这样可以延迟创建或数据加载时间,实现节省资源和提高性能。
    ​
    使用场景:
    1、减少启动时间:延迟加载启动时不需要的资源,加快程序启动时间。
    2、节省资源:有些资源可能占用较大内存或需要较长时间初始化,如果不立即加载可以节省资源。
    3、提高性能:延迟加载可以根据实际需要动态加载资源,避免了一开始就加载所有资源导致性能下降。

    2、静态代码块

    1. public class Singleton {
    2.    private static final Singleton instance;
    3.    static {
    4.        try {
    5.            instance = new Singleton();
    6.       } catch (Exception e) {
    7.            throw new RuntimeException("Exception occurred in creating singleton instance");
    8.       }
    9.   }
    10.    private Singleton() {
    11.        // 私有构造函数
    12.   }
    13.    public static Singleton getInstance() {
    14.        return instance;
    15.   }
    16. }
     
    

    这种方式跟静态常量的优缺点差不多。

    懒汉式

    1、线程不安全

    1. public class Singleton {
    2.    private  static Singleton instance;
    3.    private Singleton() {
    4.        // 私有构造函数
    5.   }
    6.    //提供一个静态的公共方法,使用到该方法时,才创建instance
    7.    //即 懒汉式
    8.    public static Singleton getInstance() {
    9.        if (instance == null) {
    10.            synchronized (Singleton.class) {
    11.                if (instance == null) {
    12.                    instance = new Singleton();
    13.               }
    14.           }
    15.       }
    16.        return instance;
    17.   }
    18. }

    优缺点说明:

    1、起到了Lazy Loading效果,但是只能在单线程中使用
    2、如果在多线程情况,一个线程进入了if(instance == null)判断语句块,还没来得及向下执行,另一个线程也通过了这个判断语句,这是会产生多个实例。
    3、总结:多线程不可使用这种方式,在实际开发中也不要使用这种方式。

    2、线程安全,同步方法

    1. public class Singleton {
    2.    private static Singleton instance;
    3.    private Singleton() {
    4.        // 私有构造函数
    5.   }
    6.    //加入了同步,解决了线程安全问题
    7.    public synchronized static Singleton getInstance() {
    8.        if (instance == null) {
    9.            instance = new Singleton();
    10.       }
    11.        return instance;
    12.   }
    13. }

    优缺点说明:

    1、解决了线程不安全问题
    2、效率太低了。每个线程想要类的实例化的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后续需要直接return就行了
    3、结论:实际开发,不推荐

    3、线程安全,同步代码块

    1. public class Singleton {
    2.   private static Singleton instance;
    3.   private Singleton() {
    4.       // 私有构造函数
    5.   }
    6.   //加入了同步,解决了线程安全问题
    7.   public static Singleton getInstance() {
    8.       if (instance == null) {
    9.       synchronized(Singleton.class){
    10.       instance = new Singleton();
    11.       }
    12.       }
    13.       return instance;
    14.   }
    15. }

    直接总结:本意是想对上种方式优化,实际并没有优化,还是会出现不同步情况。

    结论:实际开发中,不能使用这种方式。

    双重检查

    1. public class Singleton {
    2.   private volatile static Singleton instance;
    3.   private Singleton() {
    4.       // 私有构造函数
    5.   }
    6.   public static Singleton getInstance() {
    7.       if (instance == null) { // 第一次检查,避免不必要的同步
    8.           synchronized (Singleton.class) {
    9.               if (instance == null) { // 第二次检查,确保只有一个实例被创建
    10.                   instance = new Singleton();
    11.               }
    12.           }
    13.       }
    14.       return instance;
    15.   }
    16. }

    优缺点说明:

    1、Double-Check是多线程开发中常使用的,如上述代码,我们使用了两次if(singleton == null) 检查,这样就可保证线程安全了
    2、特点:线程安全、延迟加载、效率较高。推荐使用
    3、使用双重检查锁定实现线程安全的单例模式既可以确保线程安全,又可以尽量减少同步代码块的使用,从而提高性能。

    补充:

    volatile是Java的关键字。
    用于:
    1、声明变量具有可见性:
    当一个线程修改了一个被 volatile 修饰的变量的值时,这个新值对其他线程是立即可见的,即所有线程都能看到最新的值。
    ​
    2、禁止重排列:
    volatile 关键字会禁止 JVM 对代码的优化重排序,保证指令不会被重排,从而避免出现意外的执行顺序。
    ​
    3、不保证原子性:
    volatile 关键字只能确保可见性和禁止重排序,并不能保证对 volatile 变量的操作是原子性的。如果需要保证原子性

    静态内部类

    1. public class Singleton {
    2.   private Singleton() {
    3.       // 私有构造函数
    4.   }
    5.   private static class SingletonHolder {
    6.       private static final Singleton instance = new Singleton();
    7.   }
    8.   public static Singleton getInstance() {
    9.       return SingletonHolder.instance;
    10.   }
    11. }

    利用静态内部类特点:

    • 外部类被装载了,静态内部类不会被装载

    • 在调用getInstance方法的时候,会实现静态内部类只被装载一次

    优缺点说明:

    1、这种方式次啊用类装载机制来保证初始化实例时只有一个线程
    2、静态内部类在Singleton类被装载时并不会立刻实例化,而是在需要实例化时才调用getInstance方法,才会装载SingletonInstance类,从而实现Singleton的实例化
    3、类的静态属性智慧在第一次加载类的时候初始化,所以,在这JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程无法进入
    4、优点:避免了线程不安全,利用静态内部类特点实现了延迟加载,效率高
    5、结论:推荐使用

    枚举

    1. public enum Singleton {
    2.    INSTANCE;
    3.    // 可以在枚举中定义自己需要的方法和属性
    4.    public void doSomething() {
    5.        // 执行相应的操作
    6.   }
    7. }

    优缺点说明:

    1、通过枚举实现单例模式,可以避免多线程问题,还可防止反序列化重新创建新的对象。
    2、结论:推荐使用

    单例模式在JDK中使用

    比如说Runtime类中使用到了饿汉式

    小结:

    1、饿汉式
        a、静态常量(推荐)
        b、静态代码块(推荐)
    2、懒汉式
        a、线程不安全
        b、线程安全,同步方法
        c、线程安全,同步代码块
    3、双重检查(推荐)
    4、静态内部类(推荐)
    5、枚举(推荐)

    1. 饿汉式:

      • 静态常量:推荐使用,简单、线程安全,在类加载时就创建实例。

      • 静态代码块:不太推荐,与静态常量方式类似,但可读性稍差。

    2. 懒汉式:

      • 线程不安全:不推荐,多线程环境下存在安全风险。

      • 线程安全,同步方法:不推荐,性能较差,每次获取实例都需要同步。

      • 线程安全,同步代码块:一般不推荐,和同步方法类似,同步块也会影响性能。

    3. 双重检查:

      • 推荐使用:在多线程环境下,确保了延迟加载和高性能的需求。

    4. 静态内部类:

      • 推荐使用:延迟加载、线程安全、高性能,且实现简洁。

    5. 枚举:

      • 强烈推荐使用:简洁、线程安全、防止反射和序列化破坏单例,是最佳实践之一。

  • 相关阅读:
    六、stm32-OLED
    高等数学(第七版)同济大学 习题7-2 个人解答
    SLAM_语义SLAM相关论文
    MySQL---JDBC编程
    CV预测:快速使用DenseNet神经网络
    SpringMVC
    java毕业生设计校园旺角超市外卖平台计算机源码+系统+mysql+调试部署+lw
    生命在于学习——Stable Diffution(Mac端)
    案例丨妍丽联合神策上线 CDP,实现五大指标全面提升
    第四章 Istio出口流量管理
  • 原文地址:https://blog.csdn.net/qq_62074445/article/details/136193165