双重检查锁定(DCL)配合 volatile 是兼顾性能与线程安全的单例实现,必须用 volatile 防止指令重排序导致“半初始化”;枚举单例最简且天然线程安全但不支持懒加载;静态内部类方式利用类加载机制实现懒加载与线程安全。
双重检查锁定(DCL)配合 volatile 是 Java 中最常用、兼顾性能与线程安全的单例实现方式,但必须正确使用,否则仍可能出错。
为什么懒汉式直接加 synchronized 方法不行
虽然 public static synchronized Singleton getInstance() 能保证线程安全,但每次调用都阻塞,包括已初始化后的读操作——严重拖慢高并发场景下的吞吐量。真正需要同步的只有首次初始化那一小段代码。
双重检查锁定(DCL)必须用 volatile 修饰实例变量
不加 volatile 会导致指令重排序:JVM 可能将对象内存分配、构造函数执行、引用赋值三个步骤重排,使其他线程看到一个未完全构造好的 Singleton 实例(即“半初始化”状态),进而引发 NullPointerException 或逻辑错误。
正确写法:
立即学习“Java免费学习笔记(深入)”;
public class Singleton {
private static volatile Singleton instance;
<pre class="brush:php;toolbar:false;">private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
枚举单例是更简洁且天然线程安全的选择
如果单例不需要延迟加载(即类加载时就初始化),或你希望彻底规避反射/反序列化攻击,enum 是最简、最安全的方案——JVM 保证枚举实例的创建是原子且线程安全的,且无法通过反射调用私有构造器生成新实例。