设计模式之单例模式

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

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

  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或静态内部类,避免不必要的同步开销。如果单例创建成本高,可以考虑使用延迟初始化(懒汉式)。

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

相关推荐
Magnetic_h21 小时前
【iOS】设计模式复习
笔记·学习·ios·设计模式·objective-c·cocoa
A阳俊yi1 天前
设计模式——结构型模式
设计模式
Wadli1 天前
C++语法 | static静态|单例模式
开发语言·c++·单例模式
努力也学不会java1 天前
【设计模式】状态模式
java·设计模式·状态模式
.豆鲨包1 天前
【设计模式】单例模式
java·单例模式·设计模式
lpfasd1231 天前
第2课:Agent系统架构与设计模式
设计模式·系统架构
青草地溪水旁1 天前
设计模式(C++)详解—原型模式(1)
c++·设计模式·原型模式
青草地溪水旁1 天前
设计模式(C++)详解—原型模式(2)
c++·设计模式·原型模式
青草地溪水旁1 天前
设计模式(C++)详解—原型模式(3)
c++·设计模式·原型模式