单例模式和多例模式

一.单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。这种模式在需要控制资源访问,如配置文件读取、数据库连接、线程池等场景中非常有用。

1.实现要点

  1. 私有静态变量:保存类的唯一实例。
  2. 私有构造函数:防止外部通过new关键字创建实例。
  3. 公共静态方法:提供全局访问点,返回类的唯一实例。如果实例不存在,则创建它。

2.常见的实现方式

懒汉式(线程不安全)

public class SingletonLazy {  
    private static SingletonLazy instance;  
  
    private SingletonLazy() {}  
  
    public static SingletonLazy getInstance() {  
        if (instance == null) {  
            instance = new SingletonLazy();  
        }  
        return instance;  
    }  
  
    // 注意:这个实现在多线程环境下是不安全的  
}

懒汉式(线程安全)

通过在getInstance()方法上添加synchronized关键字来保证线程安全,但这样会影响性能。

public class SingletonLazyThreadSafe {  
    private static SingletonLazyThreadSafe instance;  
  
    private SingletonLazyThreadSafe() {}  
  
    public static synchronized SingletonLazyThreadSafe getInstance() {  
        if (instance == null) {  
            instance = new SingletonLazyThreadSafe();  
        }  
        return instance;  
    }  
}

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

这种方式既保证了线程安全,又提高了性能,但需要注意volatile关键字的使用来防止指令重排序。

public class SingletonDoubleChecked {  
    private static volatile SingletonDoubleChecked instance;  
  
    private SingletonDoubleChecked() {}  
  
    public static SingletonDoubleChecked getInstance() {  
        if (instance == null) {  
            synchronized (SingletonDoubleChecked.class) {  
                if (instance == null) {  
                    instance = new SingletonDoubleChecked();  
                }  
            }  
        }  
        return instance;  
    }  
}

静态内部类

这种方式利用了classloader的机制来保证实例的唯一性,并且实现了懒加载。

public class SingletonStaticInner {  
    private SingletonStaticInner() {}  
  
    private static class SingletonHolder {  
        private static final SingletonStaticInner INSTANCE = new SingletonStaticInner();  
    }  
  
    public static final SingletonStaticInner getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

枚举

这是实现单例模式的最佳方法,它更简洁,自动支持序列化机制,绝对防止多次实例化。

public enum SingletonEnum {  
    INSTANCE;  
  
    public void someMethod() {  
        // 方法实现  
    }  
}  
  
// 使用  
SingletonEnum.INSTANCE.someMethod();

3.总结

单例模式是一种简单但强大的设计模式,用于确保类只有一个实例。不同的实现方式有不同的特点和适用场景,需要根据实际需求来选择。枚举方式是推荐的首选实现方式,因为它既简洁又安全。


二.多例模式

多例模式(Multiton Pattern)是单例模式的一种变体,它允许一个类有多个实例,但实例的数量是有限且固定的。在多例模式中,通常会有一个枚举或者一个映射(如HashMap)来管理这些有限的实例。每个实例都与一个唯一的标识符(如键或枚举值)相关联。

1.实现要点

  1. 私有静态映射:用于存储所有已创建的实例,键是唯一的标识符,值是对应的实例。
  2. 私有构造函数:防止外部通过new关键字创建实例。
  3. 公共静态方法:根据传入的唯一标识符返回对应的实例。如果实例不存在,则创建它并存储在映射中。

2.示例(Java)

以下是一个使用HashMap来实现多例模式的示例

import java.util.HashMap;  
import java.util.Map;  
  
public class Multiton {  
    // 私有静态映射,用于存储所有实例  
    private static final Map instances = new HashMap<>();  
  
    // 私有构造函数  
    private Multiton() {}  
  
    // 公共静态方法,根据KeyType返回对应的实例  
    public static synchronized Multiton getInstance(KeyType key) {  
        Multiton instance = instances.get(key);  
        if (instance == null) {  
            instance = new Multiton();  
            instances.put(key, instance);  
        }  
        return instance;  
    }  
  
    // 假设的KeyType枚举,定义了所有可能的实例键  
    public enum KeyType {  
        INSTANCE1, INSTANCE2, INSTANCE3;  
    }  
  
    // 类的其他方法和属性...  
}  
  
// 使用示例  
public class Main {  
    public static void main(String[] args) {  
        Multiton instance1 = Multiton.getInstance(Multiton.KeyType.INSTANCE1);  
        Multiton instance2 = Multiton.getInstance(Multiton.KeyType.INSTANCE2);  
          
        // instance1 和 instance2 是不同的实例  
        System.out.println(instance1 == instance2); // 输出 false  
          
        // 再次获取相同的实例  
        Multiton instance1Again = Multiton.getInstance(Multiton.KeyType.INSTANCE1);  
          
        // instance1 和 instance1Again 是相同的实例  
        System.out.println(instance1 == instance1Again); // 输出 true  
    }  
}

3.注意事项

  • 线程安全:在多线程环境下,getInstance方法需要是同步的,以防止多个线程同时创建同一个键的实例。然而,同步可能会影响性能。如果实例的创建不是非常频繁,或者可以通过其他方式(如双重检查锁定)来优化同步,那么这种影响可能是可以接受的。
  • 枚举与多例模式:虽然枚举通常用于实现单例模式,但直接使用枚举来实现多例模式可能不太直观,因为枚举的实例是在类加载时创建的,并且数量是固定的。然而,你可以通过组合使用枚举和映射(或类似结构)来间接实现多例模式,但这样做可能会使代码变得复杂。
  • 性能考虑:在多例模式中,由于实例的数量是有限的,因此通常不需要担心内存使用过多的问题。然而,如果实例的创建或销毁非常昂贵(例如,涉及大量资源分配或清理),则可能需要考虑使用对象池等更高级的技术来管理这些实例。

三.区别

1.例数量

  • 单例模式:确保一个类仅有一个实例,并提供一个全局访问点。这意味着在整个应用程序的生命周期中,无论请求多少次,都只会创建和共享这一个实例。
  • 多例模式:允许一个类有多个实例,但实例的数量是有限的。这意味着根据需求,可以创建多个实例,但实例的总数受到控制,不会无限制地增长。

2.实现方式

  • 单例模式:通常通过私有静态变量来存储唯一的实例,并提供一个公共的静态方法来获取该实例。如果实例不存在,则创建它;如果已存在,则直接返回。
  • 多例模式:通常使用一个集合(如HashMap)来存储多个实例,每个实例都有一个唯一的标识符(如ID或键值)。通过提供一个公共的静态方法,并传入标识符来获取对应的实例。如果实例不存在,则根据标识符创建新的实例,并将其添加到集合中;如果已存在,则直接返回。

3.应用场景

  • 单例模式:适用于那些在整个系统中只需要一个实例的场景,如配置文件的读取、线程池、数据库连接池等。这些场景通常要求全局只有一个实例来管理资源或状态,以避免数据不一致或资源浪费。
  • 多例模式:适用于那些需要多个实例但实例数量有限的场景,如缓存系统、状态机等。在这些场景中,每个实例可能代表不同的状态或配置,因此需要独立管理。多例模式可以限制实例数量,避免资源过度使用,并复用已有实例来提高系统性能。

4.优缺点

  • 单例模式
    • 优点:提供对唯一实例的受控访问,节约系统资源,提高性能。
    • 缺点:可能导致全局状态的存在,增加代码的复杂性;难以追踪和理解的依赖关系;可能引发并发问题;阻碍扩展。
  • 多例模式
    • 优点:可以复用已有实例,避免重复创建对象,提高系统性能和资源利用率;可以灵活控制实例的生命周期。
    • 缺点:实例数量固定,难以动态地增加或减少实例数量;难以对每个实例进行单独的测试;破坏封装性,使得代码难以维护和扩展;代码复杂度高,需要考虑线程安全、序列化等问题。

你可能感兴趣的:(java)