在设计模式中,单例模式(Singleton Pattern)确保一个类只有一个实例,并提供全局访问点。它有多种实现方式,通常的8种单例模式实现方式如下:
1. 饿汉式(Eager Initialization)
这种方式在类加载时就初始化了实例,保证了线程安全,但如果实例创建时资源消耗较大,而在某些情况下并不需要实例时,会造成性能浪费。
代码示例:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2. 懒汉式(Lazy Initialization)
懒汉式在第一次调用时创建实例,懒加载的方式减少了不必要的资源消耗,但是在多线程环境下需要额外的同步机制来确保线程安全。
代码示例:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. 双重检查锁(Double-Checked Locking)
这种方式结合了懒汉式和同步机制的优点,减少了锁的开销。首先检查实例是否存在,如果没有,再加锁,保证线程安全。
代码示例:
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;
}
}
4. 静态内部类(Bill Pugh Singleton)
这种方式利用了 Java 类加载机制的特性,保证线程安全,延迟初始化,并且代码更加简洁。
代码示例:
public class Singleton {
private Singleton() {}
private static class SingletonHelper {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHelper.instance;
}
}
5. 枚举式(Enum Singleton)
这种方式利用了枚举类的特性,保证了单例的线程安全、序列化安全,同时避免了反射攻击。
代码示例:
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Singleton using enum.");
}
}
6. 登记式/容器式(Registry-Based Singleton)
这种方式通过一个容器来管理单例对象,在多线程环境下,可以保证每个对象只有一个实例。
代码示例:
import java.util.HashMap;
import java.util.Map;
public class Singleton {
private static Map<String, Singleton> instances = new HashMap<>();
private Singleton() {}
public static Singleton getInstance(String key) {
if (!instances.containsKey(key)) {
instances.put(key, new Singleton());
}
return instances.get(key);
}
}
7. Thread Local Singleton
该方式适用于每个线程都需要独立实例的情况,每个线程都拥有自己的单例对象。
代码示例:
public class Singleton {
private static ThreadLocal<Singleton> threadLocalInstance = new ThreadLocal<Singleton>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
private Singleton() {}
public static Singleton getInstance() {
return threadLocalInstance.get();
}
}
8. 使用 volatile
关键字的延迟加载方式
该方法是双重检查锁的一种改进,确保了类的实例化不被重排序,并且提高了性能。
代码示例:
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;
}
}
总结
每种单例模式都有其适用的场景和优缺点,选择哪种模式取决于具体的需求。例如,枚举式单例(Enum Singleton)是最推荐的方式,因为它既简洁又安全,能防止反射攻击,并且在 JVM 中由类加载机制确保线程安全。