
今天为大家带来的是并发设计模式实战系列,第十三章双重检查锁定(Double-Checked Locking),废话不多说直接开始~
┌───────────────────────┐ ┌───────────────────────┐
│ 第一次检查 (非同步) │──No───>│ 直接返回 │
│ (instance == null) │ └───────────────────────┘
└──────────┬────────────┘
│Yes
v
┌───────────────────────┐ ┌───────────────────────┐
│ 同步代码块 │──No───>│ 创建新实例 │
│ (synchronized) │ │ (instance = new T()) │
└──────────┬────────────┘ └──────────┬────────────┘
│Yes │
v v
┌───────────────────────┐ ┌───────────────────────┐
│ 第二次检查 │ │ 返回实例 │
│ (instance == null) │ └───────────────────────┘
└───────────────────────┘volatile保证多线程环境下的可见性系统组件 | 现实类比 | 核心行为 |
|---|---|---|
第一次检查 | 安检入口引导员 | 快速目测判断是否需要详细检查 |
同步块 | 安检门 | 严格检查每个旅客 |
第二次检查 | 安检后复核员 | 确认旅客已通过完整安检 |
public class DoubleCheckedLocking {
// 关键:volatile保证可见性和禁止指令重排序
private volatile static DoubleCheckedLocking instance;
private DoubleCheckedLocking() {
// 防止反射创建实例
if (instance != null) {
throw new IllegalStateException("Already initialized");
}
}
public static DoubleCheckedLocking getInstance() {
// 第一次检查(无锁)
if (instance == null) {
synchronized (DoubleCheckedLocking.class) {
// 第二次检查(持有锁)
if (instance == null) {
instance = new DoubleCheckedLocking();
// 对象初始化分为三步:
// 1. 分配内存空间
// 2. 初始化对象
// 3. 设置引用指向内存地址
// volatile防止步骤2和3重排序
}
}
}
return instance;
}
public static void main(String[] args) {
// 测试多线程环境
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()
+ "获取实例:" + DoubleCheckedLocking.getInstance());
}).start();
}
}
}// 错误实现示例(没有volatile)
private static Singleton instance; // 可能导致部分初始化对象被读取
// 正确实现必须包含:
// 1. volatile修饰
// 2. 双重null检查
// 3. synchronized同步块实现方式 | 线程安全 | 懒加载 | 性能 | 实现难度 |
|---|---|---|---|---|
饿汉式 | 是 | 否 | 高 | 低 |
同步方法 | 是 | 是 | 低 | 低 |
双重检查锁定 | 是 | 是 | 高 | 中 |
静态内部类 | 是 | 是 | 高 | 低 |
Enum单例 | 是 | 否 | 高 | 低 |
变量修饰符 | 保证可见性 | 防止指令重排序 | 适用场景 |
|---|---|---|---|
普通变量 | 否 | 否 | 单线程环境 |
volatile | 是 | 是 | 多线程可见性要求 |
final | 是 | 是 | 不可变对象初始化 |
synchronized | 是 | 是 | 复合操作原子性要求 |
// 替代volatile的更优方案
private static Object instance;
private static final VarHandle INSTANCE;
static {
try {
INSTANCE = MethodHandles.lookup().findVarHandle(
DoubleCheckedLocking.class, "instance", Object.class);
} catch (Exception e) {
throw new Error(e);
}
}
public static Object getInstance() {
Object localRef = instance;
if (localRef == null) {
synchronized (DoubleCheckedLocking.class) {
localRef = instance;
if (localRef == null) {
localRef = new Object();
INSTANCE.setRelease(localRef); // 比volatile更轻量级的写屏障
}
}
}
return localRef;
}private static volatile boolean initialized = false;
private DoubleCheckedLocking() {
synchronized (DoubleCheckedLocking.class) {
if (initialized) {
throw new IllegalStateException("Already initialized");
}
initialized = true;
}
}// 统计锁竞争情况
long contentionCount = DoubleCheckedLocking.class
.getClassLoader()
.getObjectMonitorUsageCount();JDK版本 | 关键改进 | 对DCL的影响 |
|---|---|---|
1.4- | 无volatile语义保障 | 完全不可用(指令重排导致失效) |
5.0+ | 增强volatile内存语义 | 标准实现可用 |
9.0+ | 引入VarHandle | 提供更优替代方案 |
15+ | 内存屏障API优化 | 可手动控制内存可见性 |
// 反例1:缺少volatile
class BrokenDCL {
private static Singleton instance; // 缺少volatile
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (BrokenDCL.class) {
if (instance == null) { // 第二次检查
instance = new Singleton(); // 可能发生指令重排序
}
}
}
return instance;
}
}
// 反例2:方法级同步
class SlowSingleton {
private static Singleton instance;
public synchronized static Singleton getInstance() { // 每次调用都同步
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}public class HolderPattern {
private HolderPattern() {}
private static class Holder {
static final HolderPattern INSTANCE = new HolderPattern();
}
public static HolderPattern getInstance() {
return Holder.INSTANCE; // 利用类加载机制保证线程安全
}
}public enum EnumSingleton {
INSTANCE;
public void businessMethod() {
System.out.println("Executing business logic");
}
}特性 | 双重检查锁定 | Enum单例 |
|---|---|---|
防反射攻击 | 需额外处理 | 天然支持 |
序列化安全 | 需重写方法 | 自动支持 |
支持继承 | 是 | 否 |
代码简洁度 | 复杂 | 极简 |
class Singleton {
private:
static std::atomic<Singleton*> instance;
static std::mutex mtx;
Singleton() = default;
public:
static Singleton* getInstance() {
Singleton* tmp = instance.load(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
tmp = instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton();
instance.store(tmp, std::memory_order_release);
}
}
return tmp;
}
};语言 | 内存顺序控制 | 等效Java特性 |
|---|---|---|
Java | volatile/happens-before | volatile |
C++ | memory_order参数 | VarHandle |
C# | volatile/Thread.MemoryBarrier | Volatile关键字 |
Go | atomic包 | AtomicReference |
Benchmark Mode Cnt Score Error Units
DCL_vs_Alternatives.dcl avgt 10 3.451 ± 0.123 ns/op
DCL_vs_Alternatives.holder avgt 10 2.893 ± 0.098 ns/op
DCL_vs_Alternatives.enum avgt 10 2.901 ± 0.105 ns/op
DCL_vs_Alternatives.synchronized avgt 10 15.672 ± 0.456 ns/op线程数 | DCL模式(QPS) | 同步方法(QPS) | 差异率 |
|---|---|---|---|
1 | 58,000,000 | 42,000,000 | +38% |
4 | 32,000,000 | 8,700,000 | +268% |
16 | 28,000,000 | 2,100,000 | +1233% |
组合模式 | 应用场景 | 示例说明 |
|---|---|---|
**+ 工厂模式** | 需要延迟创建复杂对象 | 通过DCL保证工厂实例唯一 |
**+ 装饰器** | 动态添加单例功能 | 对DCL获取的实例进行装饰 |
**+ 享元模式** | 管理共享对象池 | 用DCL控制池的初始化 |
// 模拟Spring的AnnotationAwareAspectJAutoProxyCreator
public abstract class AbstractSingletonProxyFactory {
private volatile Object proxyInstance;
protected Object getSingletonProxy() {
Object proxy = this.proxyInstance;
if (proxy == null) {
synchronized (this) {
proxy = this.proxyInstance;
if (proxy == null) {
proxy = createProxy();
this.proxyInstance = proxy;
}
}
}
return proxy;
}
protected abstract Object createProxy();
}