1. 问题场景与核心挑战
多线程环境下确保函数仅执行一次是个经典问题。想象一个电商系统在凌晨启动时,需要初始化全局配置。如果100个服务线程同时检测到配置未加载,就会引发重复初始化——轻则资源浪费,重则数据错乱。这类场景的特征是:
- 执行时机不确定:任何线程都可能成为首个触发者
- 结果全局可见性:所有线程必须立即感知完成状态
- 失败可恢复性:若执行中抛出异常,应允许其他线程重试
我曾在一个分布式日志系统中遇到真实案例:多个worker节点同时尝试创建ES索引模板,由于缺乏单次执行保障,导致模板版本冲突,最终引发日志格式混乱。这促使我深入研究各类解决方案的适用边界。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础方案:synchronized的局限性
最直观的做法是使用synchronized方法修饰:
java复制public synchronized void init() {
if (!initialized) {
doInit();
initialized = true;
}
}
缺陷分析:
- 锁粒度问题:每次调用都需获取实例锁,99%的读检查成为性能瓶颈
- 可见性问题:未正确使用volatile时,指令重排可能导致其他线程看到未初始化的状态
- 不可重入性:若init()内部递归调用自身,会引发死锁
实测数据:在100线程并发调用下,吞吐量下降至单线程的1/8。这促使我们寻找更优解。
3. 进阶方案:双重检查锁模式
双重检查锁(DCL)是经典优化方案:
java复制private volatile boolean initialized = false;
public void init() {
if (!initialized) {
synchronized (this) {
if (!initialized) {
doInit();
initialized = true;
}
}
}
}
关键改进点:
- 减少锁竞争:只有未初始化的线程会进入同步块
- **内
