-
饿汉式(Eager Initialization):在类加载时就创建实例,线程安全,但可能会造成资源浪费。
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }
-
懒汉式(Lazy Initialization):在第一次调用获取实例方法时才创建实例,可能存在线程安全问题。
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
- 双重检查锁(Double-Checked Locking):在懒汉式基础上加入双重检查锁,解决线程安全问题,避免每次获取实例都加锁静态内部类(Static Inner Class)
/** * 双重检测锁实现单例模式(使用volatile):解决指令重排可能带来的问题 * volatile只保证可见性、有序性、禁止指令重排序、不保证原子性 * 1.可见性(Visibility): * 当一个变量被声明为 volatile 后,线程在读取这个变量的值时,会直接从主内存中读取,而不是从线程的工作内存中读取。 * 任何对该变量的修改都会立即被其他线程所看到,从而确保了变量的可见性。 * * 2.禁止指令重排序(Prevents Reordering): * volatile 关键字还可以防止编译器对代码的优化,以避免指令重排序。这样可以确保代码的执行顺序符合程序员的预期。 * * 3.不保证原子性(Not Atomic): * volatile 关键字能够保证变量的可见性和禁止指令重排序,但并不能保证操作的原子性。对于涉及多步操作的情况, * volatile 不能替代 synchronized。 * * 4.适用场景: * volatile 适用于状态标志、双重检查锁定(Double-Checked Locking)、单例模式等场景,其中需要保证变量的可见性,但不需要原子性。 * * 5.内存语义: * 当一个变量被声明为 volatile 后,对这个变量的读写操作将具有内存语义,即对该变量的操作不会被重排序,保证了多线程环境下的线程安全性。 * */ public class Singleton { private Singleton() { } private static volatile Singleton instance = null; public static Singleton getInstance(){ if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
- 静态内部类(Static Inner Class):利用静态内部类的特性实现延迟加载和线程安全。
public class Singleton { private Singleton() {} public static Singleton getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } }
-
枚举(Enum):利用枚举的特性实现单例,天然线程安全,防止反射和序列化问题。
public enum Singleton { INSTANCE; // 可以添加其他方法或属性 }
6.防止反射打破单例:在单例类的构造函数中添加逻辑,以防止反射攻击并确保单例只被 创建一次:
private Singleton() {
if (instance != null) {
throw new RuntimeException("Singleton instance already exists. Use getInstance() method to get the instance.");
}
}