Java单例模式和多例模式都属于对象创建型设计模式,核心作用是控制类的实例数量,二者在实现原理上有明显的差异,适用于不同的业务场景。
单例模式的实现原理
单例模式的核心目标是确保一个类在JVM中只有一个实例,并提供一个全局访问点。其实现原理主要围绕三个要点:私有化构造方法、提供静态实例变量、提供静态获取实例的方法。
常见实现方式
饿汉式单例
饿汉式在类加载阶段就完成实例的初始化,利用类加载机制保证实例的唯一性,线程安全但可能造成资源浪费。
public class HungrySingleton {
// 类加载时就初始化实例
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 私有化构造方法,防止外部通过new创建实例
private HungrySingleton() {}
// 提供全局访问点
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
懒汉式单例(双重检查锁)
懒汉式在第一次调用获取实例方法时才初始化实例,避免提前占用资源,双重检查锁机制可以保证线程安全且效率较高。
public class LazySingleton {
// 使用volatile关键字防止指令重排
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
// 第一次检查,避免不必要的同步
if (instance == null) {
synchronized (LazySingleton.class) {
// 第二次检查,确保只有一个实例被创建
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
静态内部类单例
利用静态内部类的加载特性实现懒加载,同时保证线程安全,是推荐的单例实现方式之一。
public class InnerClassSingleton {
private InnerClassSingleton() {}
// 静态内部类,只有被调用时才会加载
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
多例模式的实现原理
多例模式的核心目标是限制一个类的实例数量,类可以有多个但固定数量的实例,通常这些实例会被提前创建并缓存起来,外部通过指定参数获取对应的实例。
实现示例
以下示例实现一个最多只有3个实例的多例类,实例会被提前初始化并存储在集合中,外部通过索引获取对应实例。
import java.util.ArrayList;
import java.util.List;
public class Multiton {
// 限制实例数量
private static final int MAX_INSTANCE_COUNT = 3;
// 缓存所有实例
private static final List<Multiton> INSTANCES = new ArrayList<>();
// 私有化构造方法
private Multiton() {}
// 静态代码块提前初始化所有实例
static {
for (int i = 0; i < MAX_INSTANCE_COUNT; i++) {
INSTANCES.add(new Multiton());
}
}
// 根据索引获取实例,索引越界则抛出异常
public static Multiton getInstance(int index) {
if (index < 0 || index >= MAX_INSTANCE_COUNT) {
throw new IllegalArgumentException("实例索引超出范围");
}
return INSTANCES.get(index);
}
}
两种模式的对比
| 对比维度 | 单例模式 | 多例模式 |
|---|---|---|
| 实例数量 | 全局唯一 | 固定多个(数量可自定义) |
| 适用场景 | 全局共享的资源类,如配置管理器、连接池 | 需要固定数量实例的场景,如枚举类、有限状态机 |
| 核心逻辑 | 私有构造+静态访问点+唯一实例 | 私有构造+实例缓存+参数化获取 |
注意事项
- 单例模式的构造方法必须私有化,否则外部可以通过
new关键字创建多个实例,破坏单例约束。 - 如果单例类需要被序列化,要重写
readResolve方法,防止反序列化时创建新的实例。 - 多例模式的实例数量需要根据业务需求合理设置,避免设置过多造成资源浪费,或设置过少无法满足业务需求。
- 两种模式都要考虑线程安全问题,尤其是在实例初始化的阶段,避免出现并发创建多个实例的情况。