1.设计模式-单例模式(Singleton Pattern)

单例模式(Singleton Pattern)是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。这对于需要控制资源使用或需要集中管理某些资源的场景非常有用,例如配置文件读取器、数据库连接池管理器等。

1.单例模式的特点

  • 唯一性:一个类只有一个实例。
  • 全局访问:提供一个全局访问点来访问该实例。

2.实现单例模式的方法

2.1 饿汉式(Eager Initialization)

“饿汉式”(Eager Initialization)是一种在软件设计模式中用于单例模式(Singleton Pattern)的实例化方式。它之所以被称为“饿汉式”,是因为它在类加载时就立即创建实例,而不是在首次使用时才创建。这种方式的名称暗示了这种急切的行为。

特点

  • 立即创建:在类加载的时候就创建实例,不管是否会用到该实例。
  • 线程安全:由于实例在类加载时就已经创建,所以在多线程环境下不会有并发问题。
  • 简单实现:实现起来比较简单,代码容易理解和维护。

缺点

  1. 资源浪费:饿汉式在类加载时就创建实例,不管这个实例是否会被使用。如果实例的创建过程比较耗资源,而实际应用中并不一定会用到这个实例,那么会造成资源浪费。
  2. 类加载时间变长:因为实例在类加载时就创建,如果实例的初始化过程比较复杂,会导致类加载时间变长,影响应用启动时间。
  3. 不支持延迟加载:饿汉式不能实现延迟加载(Lazy Loading),即只有在需要时才创建实例。如果实例创建过程很复杂并且使用频率较低,那么饿汉式不如懒汉式合适。

适用场景:

  • 在应用程序启动时就需要实例,且不会浪费资源的场景。

示例代码:

public class Singleton {
    // 在类加载时就创建实例
    private static final Singleton instance = new Singleton();

    // 私有构造函数,防止实例被创建
    private Singleton() {}

    // 提供获取实例的方法
    public static Singleton getInstance() {
        return instance;
    }

    // 示例方法
    public void someMethod() {
        System.out.println("Singleton instance method called.");
    }
}
public class Main {
    public static void main(String[] args) {
        // 获取单例实例
        Singleton singletonInstance = Singleton.getInstance();
        
        // 使用单例实例进行操作
        singletonInstance.someMethod();
    }
}

在这个例子中,Singleton 类的实例在类加载时就被创建,并保存在静态变量 instance 中。通过调用 getInstance() 方法,外部代码可以获取这个唯一的实例。

2.2 懒汉式(Lazy Initialization)

这种方式在第一次调用 getInstance 时才创建实例,能够实现延迟加载,但需要注意线程安全问题。这种方式通过在需要时才创建实例,避免了饿汉式的资源浪费问题。然而,在多线程环境下需要特别注意线程安全,可以使用同步方法或双重检查锁定来保证实例的唯一性。

优点

  1. 延迟加载:实例在第一次使用时才创建,避免了不必要的资源浪费。
  2. 按需创建:只有在需要时才创建实例,适合初始化过程复杂或耗资源的情况。

缺点

  1. 线程不安全:上面的实现并没有考虑线程安全问题,在多线程环境下可能会创建多个实例。

适用场景:

  • 单线程环境,或能保证实例方法调用时不会有多线程竞争的场景。

示例代码:

典型的懒汉式单例模式
public class Singleton {
    // 静态变量,用于保存单例实例
    private static Singleton instance;

    // 私有构造函数,防止实例被创建
    private Singleton() {}

    // 提供获取实例的方法,第一次调用时实例化对象
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    // 示例方法
    public void someMethod() {
        System.out.println("Singleton instance method called.");
    }
}

线程安全的懒汉式单例模式
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

    public void someMethod() {
        System.out.println("Singleton instance method called.");
    }
}

2.3 双重检查锁定(Double-Checked Locking)

这种方式结合了饿汉式和懒汉式的优点,既能实现延迟加载,又能保证高效的线程安全。它通过减少锁的范围来提高性能,同时确保实例在多线程环境下只被创建一次。双重检查锁定的关键是在第一次检查后再进行一次检查,以避免不必要的同步开销。

优点

  1. 高效:通过减少同步的范围,提高了性能。只有在实例未被创建时才进行同步,避免了每次获取实例时都进行同步。
  2. 线程安全:使用双重检查锁定确保了实例在多线程环境下只被创建一次,保证了线程安全性。

缺点:

  • 实现较复杂,需要确保使用 volatile 关键字。

适用场景:

  • 多线程环境,需要延迟加载和较高的访问性能的场景。

示例代码:

public class Singleton {
    // 使用 volatile 确保 instance 变量的可见性
    private static volatile Singleton instance;

    // 私有构造函数,防止实例被创建
    private Singleton() {}

    // 提供获取实例的方法
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    // 示例方法
    public void someMethod() {
        System.out.println("Singleton instance method called.");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建多个线程同时获取单例实例
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton singletonInstance = Singleton.getInstance();
                singletonInstance.someMethod();
            }).start();
        }
    }
}

关键点解释

  1. volatile 关键字
    • 使用 volatile 确保 instance 变量的可见性和有序性。volatile 保证了对变量的写操作对所有线程立即可见,防止了指令重排序的问题。
  2. 第一次检查
    • 这个检查在进入同步块之前进行,用于避免不必要的同步开销。如果实例已经存在,则直接返回实例。
  3. 同步块
    • synchronized (Singleton.class)
    • 只有在第一次检查后 instance 仍为 null 时,才进入同步块。这样可以确保只有在实例未被创建时才会进行同步,从而减少了同步开销。
  4. 第二次检查
    • 进入同步块后,再次检查 instance 是否为 null。这一步是必须的,因为在多线程环境下,可能有多个线程在第一次检查后同时进入同步块,此时需要再次检查确保实例只被创建一次。

##2.4 静态内部类(Static Inner Class)
静态内部类(Static Inner Class)是一种用于实现线程安全的懒汉式单例模式的方式。它利用类的加载机制,确保实例的延迟初始化和线程安全性,同时避免了同步带来的性能开销。

优点

  1. 延迟加载:只有在第一次调用 getInstance() 方法时,静态内部类才会被加载,从而实现延迟初始化。
  2. 线程安全:静态内部类的加载过程由 JVM 保证线程安全,不需要显式的同步。
  3. 高效:不需要同步机制,避免了同步带来的性能开销。

缺点:

  • 没有明显缺点,被认为是最好的单例实现之一。

适用场景:

  • 任何需要单例的场景,特别是需要延迟加载和线程安全的场景。

示例代码:

public class Singleton {
    // 私有构造函数,防止实例被创建
    private Singleton() {}

    // 静态内部类,持有 Singleton 实例
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供获取实例的方法
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    // 示例方法
    public void someMethod() {
        System.out.println("Singleton instance method called.");
    }
}

关键点解释

  1. 私有构造函数
    • 防止外部直接创建 Singleton 实例,确保单例模式。
  2. 静态内部类
    • private static class SingletonHolder
    • 这是一个静态内部类,持有 Singleton 类的唯一实例。该内部类只有在调用 getInstance() 方法时才会被加载。
  3. 实例的创建
    • private static final Singleton INSTANCE = new Singleton();
    • 在静态内部类中创建 Singleton 实例。由于静态变量的初始化是在类加载时进行的,JVM 会确保线程安全。
  4. 获取实例的方法
    • public static Singleton getInstance()
    • 通过调用 SingletonHolder.INSTANCE 返回单例实例。首次调用时会加载 SingletonHolder 类,并初始化 INSTANCE

2.5 枚举(Enum)单例模式

这种方式不仅能防止反射攻击,还能防止序列化导致的多实例问题,是实现单例模式的最佳实践。

为什么枚举适合单例模式?

  1. 线程安全:枚举类型本身就是线程安全的,因此不需要显式的同步处理。
  2. 防止反射攻击:枚举类不能通过反射创建实例。
  3. 防止序列化和反序列化问题:枚举类型在序列化和反序列化时能够保证实例的唯一性。
  4. 简洁和易读:代码简洁,容易理解和维护。

缺点:

  • 灵活性稍差,不能延迟加载(不过枚举单例通常不需要延迟加载)。

适用场景:

  • 需要全局唯一实例且不需要延迟加载的场景。

示例代码:

枚举的实现单例模式完整示例:

public enum Singleton {
    INSTANCE;

    // 单例实例的其他方法
    public void someMethod() {
        // 方法实现
        System.out.println("This is a singleton method.");
    }
}

如何使用上面的枚举单例:

public class SingletonTest {
    public static void main(String[] args) {
        // 访问单例实例
        Singleton singleton = Singleton.INSTANCE;
        
        // 调用单例实例的方法
        singleton.someMethod();
    }
}

详细解析

  1. 定义枚举类型public enum Singleton 定义了一个枚举类型 Singleton
  2. 单一实例INSTANCESingleton 枚举的唯一实例。通过 Singleton.INSTANCE 可以访问该实例。
  3. 方法:枚举类中可以定义方法,例如 someMethod,可以通过 Singleton.INSTANCE.someMethod() 来调用。

枚举单例的应用场景

枚举单例模式非常适合用于以下场景:

  • 配置文件管理
  • 日志系统
  • 数据库连接池
  • 缓存管理
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值