stampedlock 与 reentrantreadwritelock 的核心区别在于:前者通过乐观读(tryoptimisticread + validate)避免读锁开销,后者所有读操作均需悲观加锁;stampedlock 不支持重入,且 stamp 必须严格匹配获取与释放,适用于读多写少、读操作极轻量、可容忍重试的 jdk8+ 场景。

StampedLock 和 ReentrantReadWriteLock 到底差在哪?
StampedLock 不是“另一个读写锁”,它是对 ReentrantReadWriteLock 在读多写少 + 高并发场景下性能瓶颈的针对性补丁。核心区别在于:它用“乐观读”绕开了读锁的获取开销,而 ReentrantReadWriteLock 的读锁是悲观的——只要有人在读,写线程就得等;哪怕读操作本身极快,锁的申请/释放、队列管理、线程唤醒这些动作也吃资源。
-
ReentrantReadWriteLock读读不互斥,但读写互斥、写写互斥,且所有读操作都得走锁流程(CAS + 队列入队/出队) -
StampedLock允许“先读再验”:不加锁读一次,拿到一个long stamp表示当前版本;读完立刻用validate(stamp)检查期间有没有写发生;没被改就直接返回,改了就退回到加悲观读锁重读 - 它不支持重入:同一个线程反复调用
writeLock()或嵌套readLock()可能死锁或抛IllegalMonitorStateException
怎么安全地用乐观读?别跳过 validate
乐观读不是“随便读”,而是“读完必须校验”。漏掉 validate() 是最常见、最隐蔽的 bug 来源——你会读到脏数据,还浑然不觉。
- 正确模式:
long stamp = sl.tryOptimisticRead();→ 读字段 →if (sl.validate(stamp)) { return value; }→ 否则升级为readLock()再读一次 - 错误写法:只调
tryOptimisticRead(),不validate(),直接返回变量值 - 注意:
tryOptimisticRead()总是立即返回(非阻塞),即使锁正被写占用,它也返回一个“可能无效”的 stamp;validate()才是关键判断点 - 乐观读只适用于读取简单字段或不可变对象;涉及多个字段关联逻辑(如 balance 和 currency 必须成对读)、或需要原子性校验的场景,应直接上悲观读锁
写锁和读锁转换时 stamp 怎么传?别用错值
所有加锁方法(writeLock()、readLock()、tryOptimisticRead())都返回 stamp,所有解锁方法(unlockWrite(stamp)、unlockRead(stamp)、unlock(stamp))都要求传入对应获取时的 stamp。混用等于未解锁,最终导致写线程饥饿或读线程卡死。
-
writeLock()返回的 stamp 只能传给unlockWrite(),不能传给unlockRead() -
readLock()返回的 stamp 只能传给unlockRead() - 如果想把读锁“升级”为写锁,必须先
unlockRead(oldStamp),再writeLock();StampedLock 不支持 lock upgrade(这是有意设计,避免死锁) - stamp 是 long 类型,值为 0 表示获取失败(比如写锁被占时
tryWriteLock()返回 0),此时千万别拿 0 去调unlockWrite(0),会抛IllegalMonitorStateException
什么场景该用 StampedLock?别硬套
它不是银弹。只有当你确认满足以下全部条件时,才值得引入:
立即学习“Java免费学习笔记(深入)”;
业务模型明确是“大量读 + 极少写”,比如配置中心的本地缓存、监控指标快照、只读元数据表
读操作本身非常轻量(纳秒级字段访问),否则乐观读带来的额外
validate()开销反而抵消收益能接受“读失败后重试”的语义(即允许短时间 stale 数据,或愿意兜底重读)
项目已稳定运行在 JDK 8+,且不依赖锁重入特性(因为 StampedLock 不可重入)
替代
synchronized?没必要。简单临界区用它更重、更易出错替代
ReentrantLock?不适用。它没有单一互斥语义,只面向读写分离场景多个写线程频繁更新?慎用。乐观读验证失败率飙升,退化为频繁悲观读,性能反不如
ReentrantReadWriteLock
StampedLock 的 tricky 点全藏在 stamp 的生命周期管理里:它不像传统锁那样“获取即持有”,而是“获取即标记状态,使用即验证,释放即匹配”。一旦 stamp 流转错位,问题不会立刻报错,而是静默地让并发行为失控。











