设计模式之单例模式

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

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

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

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

1、饿汉式(静态常量)

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

Java代码示例:

java 复制代码
public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();

    private SingletonEager() {}

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

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

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

Java代码示例:

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代码示例:

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代码示例:

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代码示例:

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代码示例:

java 复制代码
public enum SingletonEnum {
    INSTANCE;

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

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

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

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

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

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

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

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

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

相关推荐
ss27313 小时前
手写MyBatis第32弹-设计模式实战:Builder模式在MyBatis框架中的精妙应用
设计模式·mybatis·建造者模式
汤姆大聪明13 小时前
【软件设计模式】策略模式
设计模式·策略模式
pengzhuofan16 小时前
Java设计模式-模板方法模式
java·设计模式·模板方法模式
使二颗心免于哀伤16 小时前
《设计模式之禅》笔记摘录 - 17.模板方法模式
笔记·设计模式·模板方法模式
AlenLi1 天前
JavaScript - 观察者模式的实现与应用场景
前端·设计模式
pengzhuofan1 天前
Java设计模式-享元模式
java·设计模式·享元模式
希望_睿智1 天前
实战设计模式之解释器模式
c++·设计模式·架构
楚禾Noah2 天前
【设计模式实战】原型模式 + 工厂模式:AI Agent 配置中心
人工智能·设计模式·原型模式
Pure_Eyes2 天前
设计模式详解
设计模式
hai_qin2 天前
三,设计模式-抽象工厂模式
c++·设计模式·抽象工厂模式