饿汉式单例模式
- 实现方式:在类加载时就创建单例实例,由于类加载机制本身是线程安全的,所以这种方式天然线程安全。
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
- 优点:实现简单,不存在线程安全问题,在类加载时就完成实例化,避免了多线程并发访问时的同步问题。
- 缺点:如果单例对象创建开销较大,而应用程序可能一直不会使用该实例,就会造成资源浪费。因为在类加载时就创建了实例,不管是否需要使用。
懒汉式(双重检查锁定)单例模式
- 实现方式:在第一次调用
getInstance
方法时才创建实例,并且通过双重检查锁定机制确保多线程环境下只有一个实例被创建。
public class LazySingleton {
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;
}
}
- 优点:延迟加载,只有在真正需要使用时才创建实例,节省资源。通过双重检查锁定,提高了并发性能,减少了不必要的同步开销。
- 缺点:实现相对复杂,需要注意
volatile
关键字的使用。如果不使用 volatile
,可能会因为指令重排序导致在未完全初始化时就被其他线程访问到。
Java 5.0及之后版本中枚举实现单例模式保证线程安全的原因
- 实现方式:
public enum EnumSingleton {
INSTANCE;
// 可以添加其他方法和属性
public void doSomething() {
System.out.println("Doing something...");
}
}
- 保证线程安全的原因:
- 类加载机制:枚举类型在Java中是特殊的类,其加载机制保证了线程安全。在类加载过程中,JVM会保证枚举实例的唯一性,并且只会实例化一次。
- 防止反射和反序列化破坏单例:枚举类型默认不支持反射创建新实例,并且在反序列化时,JVM会直接返回已有的枚举实例,而不会创建新的实例,从而保证了单例的唯一性和线程安全。