双重检查锁(dcl)必须包含锁外和锁内两次redis.get()调用:第一次在锁外快速返回缓存数据,避免无效加锁;第二次在锁内确认缓存是否已被其他线程写入,防止重复查库。缺一不可,否则退化为普通互斥锁。

双重检查锁(DCL)不是“写个锁就完事”,它必须包含两次 redis.get() 调用 —— 一次在锁外,一次在锁内。漏掉第二次检查,就等于没用 DCL,只是个普通互斥锁。
第一次检查必须在锁外,且不能省略
这是性能分水岭。99% 的请求靠这一步直接返回,根本不会走到加锁逻辑。
-
redis.get(key)返回非空,立刻 return,不进锁、不查库 - 如果跳过这步,所有请求都抢锁,锁成了性能瓶颈,反而加剧数据库压力
- 注意:这里不能用
exists替代get,因为要同时判断存在性和获取值,避免二次网络往返
第二次检查必须在 lock.lock() 之后、try 块最开头
这是线程安全的关键。锁刚拿到,不代表缓存一定还是空的 —— 可能前一个线程已写完并释放锁,但当前线程还没执行到查库那步。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 必须再次调用
redis.get(key),且结果非空时直接 return - 常见错误:把第二次检查写在查库之后,或写成
if (value == null)却没重新赋值,导致永远走查库 - 如果用 Redisson,记得锁 key 和缓存 key 分开,比如缓存 key 是
goods:1001,锁 key 应是lock:goods:1001
锁的释放必须用 finally 块包裹
否则一旦查库抛异常或网络超时,锁会一直占着,后续所有请求被阻塞,形成雪崩式卡死。
- 无论是否查到数据、是否写入缓存,只要进了
lock.lock(),就必须在finally里调lock.unlock() - Redisson 的
RLock支持自动续期,但前提是业务代码别卡死在try块里 —— 比如数据库连接池耗尽、慢 SQL、未设 timeout 的 HTTP 调用 - 本地锁(如
synchronized)无法跨 JVM,只适用于单体应用;分布式场景必须用 Redisson 或自研基于 SETNX 的锁
DCL 看似简单,真正容易出问题的地方不在锁怎么加,而在两次 redis.get() 的时机和语义是否严格满足:第一次筛流量,第二次防重复加载。少一次检查,就退化成“伪 DCL”,热点失效时数据库照样被打穿。










