自学内容网 自学内容网

单例模式的理解和实践

        在软件开发中,设计模式是开发者在特定情境下,对常见问题的通用解决方案。这些模式帮助开发者以更高效、可维护的方式编写代码。其中,单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。本文将详细讲解单例模式的理解及其在Java中的实践。

一、单例模式的理解

1.1 定义

单例模式是一种确保某个类只有一个实例,并提供一个全局访问点的设计模式。它适用于以下场景:

  • 资源控制:如数据库连接池、文件系统等,需要控制资源的使用,避免频繁创建和销毁对象。
  • 共享状态:如配置管理器、线程池等,需要在多个地方共享同一个状态。

1.2 关键点

  • 私有构造函数:防止外部通过new关键字创建实例。
  • 静态实例:在类内部维护一个静态实例。
  • 全局访问点:提供一个静态方法,用于返回该实例。

1.3 实现步骤

  1. 私有化构造函数:防止外部通过构造函数创建实例。
  2. 创建静态实例:在类内部定义一个静态变量,并初始化为类的实例。
  3. 提供静态方法:提供一个公共的静态方法,用于返回该实例。

二、单例模式的实践

        接下来,我们将通过具体的Java代码示例,演示如何实现单例模式。

2.1 饿汉式

        饿汉式在类加载时就创建实例,因此是线程安全的,但可能会造成资源浪费,如果实例从未被使用过。

// 饿汉式单例模式
public class SingletonEager {
    // 在类加载时就创建实例
    private static final SingletonEager INSTANCE = new SingletonEager();

    // 私有化构造函数
    private SingletonEager() {
        // 初始化代码
    }

    // 提供全局访问点
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}


优点

  • 线程安全,因为实例在类加载时就创建。

缺点

  • 即使实例未被使用,也会创建实例,可能造成资源浪费。

2.2 懒汉式(线程不安全)

懒汉式在第一次使用时才创建实例,节省资源,但线程不安全。

// 懒汉式单例模式(线程不安全)
public class SingletonLazyUnsafe {
    // 静态实例,初始为null
    private static SingletonLazyUnsafe instance;

    // 私有化构造函数
    private SingletonLazyUnsafe() {
        // 初始化代码
    }

    // 提供全局访问点
    public static SingletonLazyUnsafe getInstance() {
        if (instance == null) {
            instance = new SingletonLazyUnsafe();
        }
        return instance;
    }
}


优点

  • 延迟加载,节省资源。

缺点

  • 线程不安全,多个线程同时访问时,可能会创建多个实例。

2.3 懒汉式(线程安全)

通过同步方法或同步代码块,确保线程安全。

同步方法

// 懒汉式单例模式(线程安全,同步方法)
public class SingletonLazySafeSyncMethod {
    private static SingletonLazySafeSyncMethod instance;

    private SingletonLazySafeSyncMethod() {
        // 初始化代码
    }

    // 同步方法,确保线程安全
    public static synchronized SingletonLazySafeSyncMethod getInstance() {
        if (instance == null) {
            instance = new SingletonLazySafeSyncMethod();
        }
        return instance;
    }
}


同步代码块

// 懒汉式单例模式(线程安全,同步代码块)
public class SingletonLazySafeSyncBlock {
    private static SingletonLazySafeSyncBlock instance;
    private static final Object LOCK = new Object();

    private SingletonLazySafeSyncBlock() {
        // 初始化代码
    }

    // 同步代码块,提升性能
    public static SingletonLazySafeSyncBlock getInstance() {
        if (instance == null) {
            synchronized (LOCK) {
                if (instance == null) {
                    instance = new SingletonLazySafeSyncBlock();
                }
            }
        }
        return instance;
    }
}


优点

  • 延迟加载,节省资源。
  • 线程安全,通过同步确保只有一个实例。

缺点

  • 同步方法性能较差,因为每次调用都需要同步。
  • 同步代码块虽然性能有所提升,但仍然需要双重检查(Double-Checked Locking),代码相对复杂。

2.4 静态内部类(推荐)

静态内部类方式结合了饿汉式和懒汉式的优点,既实现了延迟加载,又保证了线程安全。

// 静态内部类单例模式(推荐)
public class SingletonStaticInnerClass {
    // 私有构造函数
    private SingletonStaticInnerClass() {
        // 初始化代码
    }

    // 静态内部类,负责创建实例
    private static class Holder {
        private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();
    }

    // 提供全局访问点
    public static SingletonStaticInnerClass getInstance() {
        return Holder.INSTANCE;
    }
}


优点

  • 延迟加载,节省资源。
  • 线程安全,JVM保证静态内部类在第一次使用时才加载。
  • 无需同步,性能较好。

缺点

  • 实现相对复杂,需要理解静态内部类的加载机制。

2.5 枚举(最安全)

枚举单例模式是最简单、最安全的实现方式,天生防止反射和序列化攻击。

// 枚举单例模式(最安全)
public enum SingletonEnum {
    INSTANCE;

    // 提供其他方法
    public void doSomething() {
        // 实现代码
    }
}


优点

  • 防止反射攻击,因为JVM禁止通过反射机制创建枚举实例。
  • 防止序列化攻击,因为枚举的序列化机制由JVM保证,不会创建新的实例。
  • 线程安全,由JVM保证。

缺点

  • 实现简单,但可能不适合所有场景,特别是需要继承其他类的场景(Java枚举不能继承非枚举类)。

三、单例模式的注意事项

防止反射攻击:通过私有构造函数和异常处理,防止通过反射机制创建实例。

private Singleton() {
    if (instance != null) {
        throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
    // 初始化代码
}

防止序列化攻击:通过实现readResolve方法,防止通过序列化机制创建实例。

private Object readResolve() throws ObjectStreamException {
    return INSTANCE;
}

线程安全:确保在多线程环境下,只有一个实例被创建。

延迟加载:如果实例创建开销较大,考虑使用懒汉式或静态内部类方式。

总结

        单例模式是一种确保某个类只有一个实例,并提供一个全局访问点的设计模式。它通过私有化构造函数、创建静态实例和提供全局访问点来实现。在Java中,有多种实现方式,包括饿汉式、懒汉式(线程不安全、线程安全)、静态内部类和枚举。每种方式都有其优缺点,开发者应根据具体场景选择合适的实现方式。同时,需要注意防止反射和序列化攻击,确保线程安全。通过合理使用单例模式,可以提高代码的可维护性和性能。


原文地址:https://blog.csdn.net/huaqianzkh/article/details/144175077

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