性能对比
- 饿汉式
- 性能:在类加载时就创建实例,不存在线程同步问题,所以性能上没有因同步带来的额外开销。但如果该单例实例一直未被使用,就会造成内存浪费。
- 示例代码:
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
- 懒汉式(线程安全版本)
- 性能:在第一次调用
getInstance
方法时创建实例,解决了饿汉式可能的内存浪费问题。然而,由于使用synchronized
关键字修饰getInstance
方法,每次调用该方法都需要进行线程同步,这在高并发场景下会成为性能瓶颈。
- 示例代码:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
- 双重检查锁定
- 性能:既实现了延迟加载,又避免了不必要的同步。在第一次检查
instance == null
时,多数情况下可以直接返回实例,只有在实例未创建时才会进入同步块。并且在同步块内再次检查instance == null
,确保多线程环境下只创建一次实例。这种方式在高并发场景下性能较好。
- 示例代码:
public class DoubleCheckedLockingSingleton {
private volatile static DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
- 静态内部类
- 性能:利用了类加载机制实现延迟加载,并且保证线程安全。在外部类被加载时,静态内部类不会被加载,只有在调用
getInstance
方法时,静态内部类才会被加载并创建实例。这种方式既保证了延迟加载,又没有额外的同步开销,性能较好。
- 示例代码:
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
private static class InnerClass {
private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return InnerClass.instance;
}
}
- 枚举
- 性能:枚举实现单例模式非常简洁,并且由JVM保证其线程安全和实例唯一性。在序列化和反序列化时也能保证单例特性,性能上没有同步等额外开销。
- 示例代码:
public enum EnumSingleton {
INSTANCE;
// 可以添加其他方法和属性
public void doSomething() {
System.out.println("Doing something...");
}
}
不同场景下的选择
- 应用场景一:对启动性能要求高,且单例实例一定会被使用
- 选择:饿汉式。因为它在类加载时就创建实例,没有延迟加载的开销,对于启动性能有一定提升,并且不会造成内存浪费。
- 应用场景二:对内存比较敏感,且并发访问不高
- 选择:懒汉式(线程安全版本)。它实现了延迟加载,能节省内存,虽然每次获取实例都有同步开销,但在并发访问不高的情况下,对性能影响不大。
- 应用场景三:高并发场景,且对延迟加载有要求
- 选择:双重检查锁定或静态内部类。双重检查锁定通过减少不必要的同步,在高并发下有较好性能;静态内部类利用类加载机制保证延迟加载和线程安全,同样适用于高并发场景。
- 应用场景四:需要考虑序列化和反序列化的场景
- 选择:枚举。它在序列化和反序列化时能保证单例特性,不需要额外处理。