单例模式(Singleton Pattern)是设计模式中最为经典的模式之一,其核心思想是确保一个类只有一个实例,并提供一个全局访问点。单例模式在实际开发中非常常见,比如配置管理器、日志记录器、线程池等场景。本文将详细探讨 Java 中单例模式的 10 种实现方式,并分析每种方式的优缺点。
单例模式的实现需要满足以下三个条件:
new
关键字创建实例。单例模式可以分为“懒汉式”和“饿汉式”两大类,其中懒汉式在需要时才创建实例,而饿汉式在类加载时就创建实例。此外,还有一些更高级的实现方式,比如使用枚举、静态内部类等。
懒汉式单例是最简单的实现方式,它在第一次调用 getInstance()
方法时才创建实例。这种方式的优点是延迟加载,但缺点是线程不安全。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
为了保证线程安全,可以在 getInstance()
方法上加 synchronized
关键字。
public class ThreadSafeLazySingleton {
private static ThreadSafeLazySingleton instance;
private ThreadSafeLazySingleton() {}
public static synchronized ThreadSafeLazySingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeLazySingleton();
}
return instance;
}
}
getInstance()
都需要加锁,性能较差。双重检查锁定是一种优化的懒汉式实现方式,它通过两次检查实例是否为 null
来减少锁的使用,从而提高性能。
public class DoubleCheckedSingleton {
private static volatile DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
volatile
的作用。饿汉式单例在类加载时就创建实例,因此是线程安全的,但无法实现延迟加载。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
静态内部类是一种优雅的单例实现方式,它利用 Java 的类加载机制来保证线程安全,同时实现延迟加载。
public class StaticInnerClassSingleton {
private static class Holder {
private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
}
private StaticInnerClassSingleton() {}
public static StaticInnerClassSingleton getInstance() {
return Holder.instance;
}
}
枚举是 Java 中实现单例的一种推荐方式,它天然支持线程安全,并且可以防止反射攻击。
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("EnumSingleton is working");
}
}
容器式单例适用于管理多个单例对象的场景,通常使用 Map
来存储实例。
public class ContainerSingleton {
private static Map<String, Object> instances = new ConcurrentHashMap<>();
private ContainerSingleton() {}
public static Object getInstance(String className) {
if (!instances.containsKey(className)) {
synchronized (ContainerSingleton.class) {
if (!instances.containsKey(className)) {
try {
instances.put(className, Class.forName(className).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return instances.get(className);
}
}
为了防止反射破坏单例模式,可以在构造方法中抛出异常。
public class ReflectionSafeSingleton {
private static final ReflectionSafeSingleton instance = new ReflectionSafeSingleton();
static {
// 防止反射攻击
ReflectionSafeSingleton.class.getDeclaredConstructors()[0].setAccessible(false);
}
private ReflectionSafeSingleton() {
if (instance != null) {
throw new RuntimeException("Singleton instance already exists");
}
}
public static ReflectionSafeSingleton getInstance() {
return instance;
}
}
为了防止序列化和反序列化破坏单例,可以重写 readResolve
方法。
public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static final SerializableSingleton instance = new SerializableSingleton();
private SerializableSingleton() {}
public static SerializableSingleton getInstance() {
return instance;
}
// 防止反序列化创建新实例
protected Object readResolve() {
return instance;
}
}
Serializable
接口。单例模式的实现方式多种多样,每种方式都有其适用场景和优缺点。在实际开发中,可以根据需求选择合适的实现方式:
理解单例模式的多种实现方式及其优缺点,可以帮助我们更好地应对实际开发中的各种需求。