自学内容网 自学内容网

设计模式之单例模式

单例模式是一种创建型设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点。这种模式常用于那些需要频繁创建和销毁的资源消耗较大的对象,或者需要严格控制实例数量的对象,例如数据库连接池、缓存、日志管理器等。

单例模式的核心要点包括:

  1. 确保单例:通过私有构造函数、静态内部类、双重检查锁定(DCL)、枚举等机制,防止外部创建额外的实例。
  2. 全局访问:提供一个公共的静态方法(通常称为getInstance()),允许客户端在任何地方通过此方法获取单例对象。

单例模式有多种实现方式,以下是常见的几种:

1、饿汉式(静态常量)

        在类加载时立即创建单例对象,线程安全,但可能造成资源浪费(如果单例在整个程序生命周期内并不需要,却在类加载时就被创建)。

Java代码示例:

public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();

    private SingletonEager() {}

    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

2、懒汉式(线程不安全)

        在首次调用getInstance()时创建单例对象,非线程安全。

Java代码示例:

public class SingletonLazyUnsafe {
    private static SingletonLazyUnsafe instance;

    private SingletonLazyUnsafe() {}

    public static SingletonLazyUnsafe getInstance() {
        if (instance == null) {
            instance = new SingletonLazyUnsafe();
        }
        return instance;
    }
}

3、懒汉式(线程安全,同步方法或同步块)

        在首次调用getInstance()时创建单例对象,并通过同步方法或同步块确保线程安全,但每次获取实例都需要同步,影响性能。

Java代码示例:

public class SingletonLazySynchronizedMethod {
    private static SingletonLazySynchronizedMethod instance;

    private SingletonLazySynchronizedMethod() {}

    public static synchronized SingletonLazySynchronizedMethod getInstance() {
        if (instance == null) {
            instance = new SingletonLazySynchronizedMethod();
        }
        return instance;
    }
}

4、双重检查锁定(DCL,推荐使用)

        在getInstance()方法中进行两次检查(一次检查实例是否已创建,一次检查是否需要同步),既保证线程安全又减少了同步带来的性能损耗。

Java代码示例:

public class SingletonDCL {
    private volatile static SingletonDCL instance;

    private SingletonDCL() {}

    public static SingletonDCL getInstance() {
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

5、静态内部类

        利用类加载机制保证线程安全,实现简单且性能良好。

Java代码示例:

public class SingletonInnerClass {
    private SingletonInnerClass() {}

    private static class SingletonHolder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }

    public static SingletonInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

6、枚举

        JDK 1.5 引入的实现方式,简洁且天生线程安全。

Java代码示例:

public enum SingletonEnum {
    INSTANCE;

    // 可以在此处添加其他方法和属性
}

使用过程中需要注意的问题

  1. 线程安全性:确保单例在多线程环境下的正确性,避免多个实例被创建。饿汉式、静态内部类和枚举实现天生线程安全,懒汉式和DCL需要特别关注线程安全问题。

  2. 序列化与反序列化:若单例类实现了Serializable接口,可能通过反序列化创建新的实例。为防止这种情况,需在单例类中添加readResolve()方法,返回唯一的单例实例。

  3. 反射攻击:恶意代码可以通过反射机制调用私有构造函数创建新的实例。为防止这种情况,可以在构造函数中添加防御性编程,如抛出异常或设置标志位验证单例状态。

  4. 测试:单例模式可能导致单元测试困难,因为它与其他组件紧密耦合。可以考虑使用依赖注入框架或提供一个测试友好的构造函数(仅在测试环境中启用)来解决这个问题。

  5. 资源释放:如果单例持有昂贵资源(如数据库连接、文件句柄等),需要确保在应用程序退出时正确释放资源,避免资源泄漏。可以使用java.lang.Runtime.addShutdownHook()注册关闭钩子,或使用try-finally语句确保资源清理。

  6. 性能:在高并发场景下,选择性能更好的实现方式,如DCL或静态内部类,避免不必要的同步开销。如果单例创建成本高,可以考虑使用延迟初始化(懒汉式)。

单例模式在确保一个类仅有一个实例的同时提供了全局访问点,适用于需要控制实例数量、共享资源或协调系统行为的场景。在实现和使用单例模式时,要注意线程安全性、序列化与反序列化、反射攻击、测试、资源释放和性能等方面的问题。根据实际需求选择合适的实现方式。


原文地址:https://blog.csdn.net/Rcain_R/article/details/137977992

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!