单例模式的实现方式和优缺点
单例模式(Singleton Pattern)是一种设计模式,确保一个类在整个程序的生命周期内只能被实例化一次,并且提供一个全局访问该实例的入口。单例模式通常用于管理共享资源或状态的场景,如数据库连接池、线程池、日志记录器等。
单例模式的实现方式
懒汉式(Lazy Initialization) 在需要时才创建实例,延迟加载。
public class Singleton {
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;
}
}
优点:
线程安全,且只在实例未创建时才加锁,提高了性能。
缺点:
实现较为复杂,涉及到 Java 内存模型和指令重排的问题,需要使用
volatile
关键字。
饿汉式(Eager Initialization) 在类加载时就创建实例,不管是否需要使用。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优点:
实现简单,线程安全,因为类加载机制天然保证实例的唯一性。
缺点:
提前占用系统资源,无法延迟加载。如果实例不使用会造成资源浪费。
静态内部类(Static Inner Class) 使用静态内部类来实现延迟加载且线程安全的单例模式。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:
线程安全,利用类加载机制实现延迟加载。
性能优于同步方法和双重检查锁定,且实现简单。
缺点:
相比其他实现稍微复杂一些,主要在理解上。
枚举(Enum Singleton) 使用枚举来实现单例模式。
public enum Singleton {
INSTANCE;
}
优点:
天然线程安全。
防止反射和序列化破坏单例。
实现最简单,使用 Java 枚举类型。
缺点:
无法延迟加载。
不太符合传统面向对象编程风格,有时可能不适合复杂需求的场景
单例模式的优缺点
优点:
控制实例数量:确保系统中某个类的实例唯一,节省内存。
全局访问点:提供统一的访问入口,方便管理全局状态。
线程安全:某些实现方式能够确保多线程环境下的安全性。
缺点:
难以扩展:因为单例类通常不允许被继承,限制了扩展的灵活性。
隐藏依赖关系:单例模式的类可能会变得过于复杂,隐藏了实际的依赖关系,增加了耦合性。
难以测试:由于全局状态的存在,单例模式中的代码难以进行单元测试,可能导致测试之间的互相干扰。