单例模式vs 多例模式

单例模式是种设计模式,旨在确保一个类只有一个实例存在,并提供对该实例的全局访问。在实际应用中,有些对象只需要一个实例,例如全局的配置、日志记录器等,此时使用单例模式可以避免创建多个实例造成资源的浪费和不必要的麻烦。

经典案例(线程安全+懒加载)

public class SingleTon{
  private SingleTon(){}
 
  private static class SingleTonHoler{
     private static SingleTon INSTANCE = new SingleTon();
 }
 
  public static SingleTon getInstance(){
    return SingleTonHoler.INSTANCE;
  }}

在这个实现中,通过将构造函数私有化,外部无法直接创建类的实例。同时,使用一个私有静态内部类 SingleTonHolder 来维护一个私有静态成员变量 INSTANCE,并在该内部类的静态初始化器中实例化 SingleTon 对象。这样做的好处是,在 getInstance() 方法第一次被调用时才会触发 SingleTonHolder.INSTANCE 的初始化,也就是实现了懒汉式的单例模式,避免了不必要的资源浪费。

此外,由于类的初始化是线程安全的,这种实现方式不需要显式地进行线程同步,因此也避免了多线程环境下的竞态条件问题。同时,由于将内部类作为单例模式的持有者,使得实现代码更加简洁和易于维护。

总的来说,这种实现方式是一种经典的单例模式实现,具有线程安全、延迟加载、代码简洁等优点,也是一种较为推荐的单例模式实现方式。

细节:该单例模式的实现方式是基于静态内部类实现的,静态内部类只会被加载一次,因此只有一个单例对象实例。即使有多个线程同时访问getInstance()方法,也不会产生多个实例,因为静态内部类的加载和初始化都是线程安全的,且只会加载一次

单例模式可以分为懒汉式和饿汉式两种实现方式。

懒汉式是指在第一次使用时才会创建实例,而饿汉式则是在类加载时就创建实例。

在懒汉式中,由于只有在需要时才会创建实例,因此可以避免不必要的资源浪费,但是在多线程环境下需要考虑线程安全问题,需要使用线程同步机制来保证只有一个线程创建实例。

在饿汉式中,由于在类加载时就创建实例,因此可以避免多线程环境下的线程安全问题,但是可能会造成不必要的资源浪费,而且也无法实现懒加载,即使该实例从未被使用,也会一直存在于内存中。

因此,在实际应用中,需要根据具体的情况来选择懒汉式或饿汉式的实现方式。如果需要在系统中创建多个单例对象,或者创建单例对象的成本比较低,可以考虑使用饿汉式实现;如果需要创建的单例对象较多,或者创建单例对象的成本比较高,可以考虑使用懒汉式实现。同时,还可以根据具体的需求考虑使用双重检查锁、枚举等其他实现方式

饿汉

public class Singleton {
    // 在类加载时即创建实例,保证线程安全
    private static Singleton instance = new Singleton();

    // 将构造函数私有化,禁止外部直接创建实例
    private Singleton() {}

    // 提供全局访问点,返回唯一的实例
    public static Singleton getInstance() {
        return instance;
    }
}

在这个示例中,instance 在类加载时即被创建,因此保证了线程安全,避免了多线程环境下的竞态条件问题。同时,由于实例的创建是在静态初始化器中完成的,因此可以保证实例的唯一性。

这种实现方式的优点是代码简洁,且在多线程环境下不需要考虑线程同步问题,因此具有一定的效率优势。但是缺点是无法实现懒加载,即使该实例从未被使用,也会一直存在于内存中,因此可能会造成不必要的资源浪费。

懒汉

public class LazySingleton {
    private static LazySingleton instance = null;

    private LazySingleton() {
        // 私有构造函数,避免外部类创建对象实例
    }

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

在该示例中,LazySingleton 类只有在第一次调用 getInstance 方法时才会创建一个新的对象实例。因为在静态方法 getInstance 中,我们使用 synchronized 关键字来保证多线程环境下只有一个线程可以访问该方法,避免了线程安全问题。当实例不存在时,创建新的实例并返回;否则直接返回现有实例。

需要注意的是,该实现虽然避免了在程序启动时就创建对象实例的问题,但是每次调用 getInstance 方法都需要进行同步处理,影响了性能。可以采用双重检查锁定(Double-Check Locking)的方式来提高性能,避免重复的同步操作。

单例模式对应的是多例模式(Multiton Pattern),也被称为对象池(Object Pool)模式。多例模式是一种创建型设计模式,它允许我们通过创建一个有限的对象池来重复使用相同的对象实例。

多例模式

在多例模式中,每个实例都有一个唯一的名字或标识符,通过标识符可以获取到相应的实例。与单例模式不同的是,多例模式可以创建多个实例,每个实例都有自己的状态,并且可以在需要的时候从对象池中获取实例来处理业务逻辑。

一个常见的多例模式的例子是数据库连接池。在大多数应用程序中,数据库连接是一种昂贵的资源,每个请求都需要创建一个连接会导致性能下降。为了避免这种情况,我们可以创建一个有限的连接池,并在需要连接数据库时从连接池中获取一个可用连接,使用完毕后再将连接返回到连接池中,这样就可以避免频繁地创建和销毁数据库连接,从而提高了系统的性能。

public class Multiton {
    private static final int MAX_INSTANCES = 3;
    private static final Map instances = new HashMap<>();
    private static int instanceCount = 0;
    
    private Multiton() {}
    
    public static Multiton getInstance(int instanceNumber) {
        if (instanceNumber < 1 || instanceNumber > MAX_INSTANCES) {
            throw new IllegalArgumentException("Invalid instance number");
        }
        
        if (!instances.containsKey(instanceNumber)) {
            if (instanceCount < MAX_INSTANCES) {
                instances.put(instanceNumber, new Multiton());
                instanceCount++;
            } else {
                throw new RuntimeException("Maximum number of instances exceeded");
            }
        }
        
        return instances.get(instanceNumber);
    }
}

在这个例子中,Multiton 类的构造方法被私有化,因此只能通过 getInstance 方法获取实例。该方法接收一个 instanceNumber 参数,该参数指示要获取的实例的编号。instances 变量是一个静态的、包含实例的 HashMap,用于存储已经创建的实例。instanceCount 是一个静态变量,用于跟踪已经创建的实例数量,最大值为 MAX_INSTANCES。当调用 getInstance 方法时,首先检查 instanceNumber 参数是否合法,然后检查实例是否已经存在于 instances 中。如果实例不存在,则检查是否已经创建了最大数量的实例,如果没有,则创建一个新的实例并将其添加到 instances 中,否则抛出异常。最后,返回与 instanceNumber 参数匹配的实例。

为什么getInstance 静态

在这个例子中,Multiton 类的构造方法被私有化,因此只能通过 getInstance 方法获取实例。该方法接收一个 instanceNumber 参数,该参数指示要获取的实例的编号。instances 变量是一个静态的、包含实例的 HashMap,用于存储已经创建的实例。instanceCount 是一个静态变量,用于跟踪已经创建的实例数量,最大值为 MAX_INSTANCES。当调用 getInstance 方法时,首先检查 instanceNumber 参数是否合法,然后检查实例是否已经存在于 instances 中。如果实例不存在,则检查是否已经创建了最大数量的实例,如果没有,则创建一个新的实例并将其添加到 instances 中,否则抛出异常。最后,返回与 instanceNumber 参数匹配的实例。

你可能感兴趣的:(单例模式,java,开发语言)