StampedLock 详解

0 阅读17分钟

StampedLock 是 Java 8 引入的一种新型锁机制,位于 java.util.concurrent.locks 包。它的核心设计目标是通过乐观读模式极大提升读操作的并发度

StampedLock 提供了三种锁模式:

  • 写锁(独占)
  • 悲观读锁(共享)
  • 乐观读(无锁,仅获取版本戳)

此外,它具有以下特点:

  • 不可重入(写锁不可重入,读锁虽可多次获取但官方不推荐)
  • 支持锁升级(读锁 → 写锁)和降级(写锁 → 读锁 → 乐观读)
  • 不支持条件变量Condition
  • 内部基于 64 位 state 状态字CLH 队列 实现,通过位运算高效管理锁状态

适用场景:读操作极其频繁、写操作很少、读操作非常简短(微秒级) 的场景,如缓存统计、内存数值快照、实时数据管道等。


1. 核心特性及其实现原理

1.1 特性一:三种锁模式(写锁、悲观读锁、乐观读)

设计目标:提供比传统读写锁更细粒度的并发控制,尤其是允许读操作在不加锁的情况下尝试读取,从而避免读-写互斥。

① 写锁(独占)实现
public long writeLock() {
    long s, next;
    return ((((s = state) & ABITS) == 0L &&   // 无任何锁(无读锁且无写锁)
             U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
            next : acquireWrite(false, 0L));
}
  • ABITS = 0xFF(低 8 位掩码),(state & ABITS) == 0 表示低 8 位全为 0,即没有写锁(bit8=0)且没有读锁(bit7及以上为0)
  • 满足条件则 CAS 将 state 增加 WBIT=256,使写锁位置 1,返回新 state 作为 stamp。
  • 否则进入 acquireWrite 排队自旋/阻塞。

释放写锁

public void unlockWrite(long stamp) {
    if (state != stamp || (stamp & WBIT) == 0L)
        throw new IllegalMonitorStateException();
    U.putLongVolatile(this, STATE, stamp + WBIT); // 翻转 WBIT 位
}

释放时直接加 WBIT,使写锁位翻转(1→0),同时也改变了高位版本,用于乐观读验证。

② 悲观读锁(共享)实现
public long readLock() {
    long s = state, next;
    return ((w = (int)(s & WBIT)) == 0L &&   // 无写锁
            (next = s + RUNIT) < RFULL ?     // 读锁计数未超过127
            U.compareAndSwapLong(this, STATE, s, next) ? next : acquireRead(false, 0L) :
            acquireRead(false, 0L));
}
  • RUNIT = 128,每次获取读锁 state 增加 128,释放时减去 128。
  • RFULL = 127 表示最多支持 127 个并发读锁(超过则进入 acquireRead 慢路径)。
  • 无写锁且计数未满时 CAS 更新 state,返回新 state 作为 stamp。

释放读锁

public void unlockRead(long stamp) {
    long s, m;
    while (((s = state) & SBITS) != (stamp & SBITS) ||
           (m = s & ABITS) == 0L || m == WBIT)
        throw new IllegalMonitorStateException();
    if (U.compareAndSwapLong(this, STATE, s, s - RUNIT))
        return;
    else
        // 缓慢路径:可能需唤醒等待的写线程
}
③ 乐观读实现
public long tryOptimisticRead() {
    long s;
    return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}
  • 如果当前没有写锁,则返回 state & SBITS,即去掉低 7 位后的高位值(版本信息)。
  • 如果有写锁,返回 0(表示乐观读无效)。

验证乐观读

public boolean validate(long stamp) {
    U.loadFence();  // 加载屏障,禁止读操作重排序
    return (stamp & SBITS) == (state & SBITS);
}
  • 比较传入 stamp 的高位与当前 state 的高位是否相等。
  • 若相等,说明从 tryOptimisticReadvalidate 之间没有发生过写锁的获取与释放(因为每次写锁操作都会翻转 WBIT 位,改变高位)。

数据结构支撑:state 的高位作为版本戳,使得乐观读无需任何 CAS 或阻塞即可检测写锁是否发生。


1.2 特性二:不可重入

设计目标:简化锁状态,提高性能,避免维护重入计数带来的开销。

源码验证写锁不可重入

public long writeLock() {
    // 只有 state & ABITS == 0 才能获取,即不能有任何锁
    // 如果当前线程已经持有写锁,则 state & ABITS != 0,直接进入 acquireWrite
    ...
}

当线程已持有写锁时,(state & ABITS) 的低 8 位非 0(写锁位为 256),因此 CAS 条件失败,调用 acquireWriteacquireWrite 会将当前线程放入等待队列,导致自己等待自己释放锁,形成死锁。

读锁的可重入性(非官方推荐): 虽然同一线程可以多次调用 readLock()(每次都会增加 RUNIT),但这不是设计意图。因为 StampedLock 不会记录线程身份,释放时需要调用相同次数的 unlockRead,容易出错。官方文档明确建议不要将 StampedLock 用于可重入场景

数据结构支撑:state 中没有维护持有锁的线程 ID 或重入计数,只有简单的位标志和读锁计数,因此无法支持重入检测。


1.3 特性三:锁升级与降级

设计目标:允许线程在不释放读锁的情况下直接升级为写锁(条件满足时),避免释放后重新竞争带来的性能损失和死锁风险。

升级(读锁 → 写锁)
public long tryConvertToWriteLock(long stamp) {
    long a = stamp & ABITS, m, s, next;
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        if ((m = s & ABITS) == 0L) {       // 无锁
            if (a != 0L) break;
            if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
                return next;
        } else if (m == WBIT) {            // 已经是写锁
            if (a != m) break;
            return stamp;
        } else if (m == RUNIT && a != 0L) { // 只有一个读锁(读锁计数为1)
            if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT + WBIT))
                return next;
        }
        break;
    }
    return 0L;
}
  • 只有当当前线程持有唯一的一个读锁s & ABITS == RUNIT)时,才能成功升级。
  • 升级操作将 state 减去 RUNIT 再加上 WBIT,原子地完成读锁释放和写锁获取。
  • 升级失败返回 0,调用者需释放原读锁并重新获取写锁。
降级(写锁 → 读锁)
public long tryConvertToReadLock(long stamp) {
    long a = stamp & ABITS, m, s, next;
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        if ((m = s & ABITS) == 0L) {
            if (a != 0L) break;
            if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                return next;
        } else if (m == WBIT) {            // 持有写锁
            if (a != m) break;
            next = s - WBIT + RUNIT;
            if (U.compareAndSwapLong(this, STATE, s, next))
                return next;
        } else if (a != 0L && a < WBIT) {  // 已经是读锁
            return stamp;
        }
        break;
    }
    return 0L;
}
  • 从写锁降级为读锁时,state 减去 WBIT 再加上 RUNIT,写锁释放,读锁计数增加 1。
  • 降级后当前线程仍然持有锁(现在是读锁),可以继续读取共享数据。

数据结构支撑:state 同时编码了写锁标志和读锁计数,使得原子转换成为可能。通过一个 CAS 操作即可完成锁模式的切换,无需额外同步。


1.4 特性四:无条件变量(Condition)

StampedLock 没有提供 newCondition() 方法。这是因为条件变量需要与锁的重入计数和等待队列深度集成,而 StampedLock 的设计目标是轻量和高性能,不支持重入。如果需要等待/通知,应使用其他同步机制(如 ReentrantLock + Conditionsynchronized)。

数据结构支撑:CLH 队列只用于锁获取的排队,没有维护条件队列所需的节点状态和信号机制。


1.5 特性五:非公平调度

StampedLock 默认是非公平的:新线程可以在队列头线程之前插队获取锁(写锁在无竞争时)。源码中没有公平模式的选项。

实现体现

  • writeLock()readLock() 在尝试获取时,不会检查队列中是否有等待线程,只要 state 条件满足就直接 CAS 获取。
  • 仅在 CAS 失败后才进入 acquireWrite/acquireRead 排队。

非公平调度可能导致写锁饥饿,但乐观读的存在使得读线程大部分时间不阻塞写线程,因此写锁饥饿问题相对较轻。

数据结构支撑:CLH 队列只负责管理无法立即获取锁的线程,不干预新线程的插队行为。


1.6 特性六:高性能根源

核心:乐观读完全无锁,只涉及两次 volatile 读(tryOptimisticReadvalidate),不进行任何 CAS 或上下文切换。在多核 CPU 上,乐观读的开销极低(约 10-20 纳秒),而悲观读锁至少需要一次 CAS(约 50-100 纳秒)。

数据结构支撑

  • state 的高位作为版本戳,使得验证操作仅需比较两个 long 值。
  • 没有维护读锁的线程归属,减少了内存开销和缓存一致性流量。

2. 内部状态与队列结构设计(如何支撑上述特性)

StampedLock 的核心数据结构是一个 64 位 volatile long 类型的 state 和一个 CLH 双向队列

2.1 state 的位布局设计

// 常量定义(JDK 11)
private static final int LG_READERS = 7;
private static final long RUNIT = 1L << LG_READERS;      // 0x80 = 128
private static final long WBIT  = 1L << (LG_READERS + 1); // 0x100 = 256
private static final long RBITS = RUNIT - 1;              // 0x7F = 127
private static final long RFULL = RBITS;                  // 最大读锁计数 127
private static final long ABITS = RBITS | WBIT;           // 低 8 位掩码 0xFF
private static final long SBITS = ~RBITS;                 // 保留高位(不含低 7 位)

位域含义

位范围含义
bit 7 (128)RUNIT每个读锁的单位增量。读锁计数 = (state / 128) 的低位部分
bit 8 (256)WBIT写锁标志位。写锁持有时该位为 1
高 56 位无直接常量存储读锁的扩展计数(当读锁计数 >127 时)和版本信息

实际状态编码

  • 无锁state & ABITS == 0(低 8 位全 0)
  • 写锁持有state & WBIT != 0(第 8 位为 1),且低 8 位中只有第 8 位是 1(即 state & ABITS == WBIT
  • 读锁计数(state & SBITS) / RUNIT(忽略低 7 位)。每次 readLock() 将 state 增加 RUNIT,释放时减少。

设计意图

  • 支持三种模式:通过两个位域(写锁位 + 读锁计数)即可区分无锁、写锁、读锁状态。
  • 乐观读版本戳tryOptimisticRead 返回 state & SBITS(高位),validate 比较高位是否变化。每次写锁的获取与释放都会增加 WBIT(翻转第 8 位),从而改变高位,使得验证失败。
  • 原子锁升级/降级:因为 state 中写锁位和读锁计数是独立的,可以通过一次 CAS 完成加减 RUNITWBIT 的复合操作。

2.2 CLH 队列设计

当线程无法立即获取锁时,会进入 acquireWriteacquireRead,将自身包装为 WNode 节点加入队列。

static final class WNode {
    volatile WNode prev;
    volatile WNode next;
    volatile Thread thread;
    volatile int status;   // 0: 等待, 1: 取消, -1: 唤醒后继
    final int mode;        // 0: 读节点, 1: 写节点
}
  • 队列是双向的,头节点 whead 代表当前锁持有者(或虚拟头),尾节点 wtail 用于快速添加。
  • mode 区分读/写节点,用于决定唤醒策略(例如读节点可以级联唤醒)。
  • status 用于取消和传递唤醒信号。

设计意图

  • 管理竞争失败的线程,避免无限制自旋消耗 CPU。
  • 支持读写锁的调度:当锁释放时,根据队列头节点的 mode 决定唤醒读节点还是写节点。
  • 与 state 协同工作。

2.3 数据结构如何共同支撑特性总结

特性state 的作用CLH 队列的作用
三种锁模式编码写锁位、读锁计数、版本戳管理竞争失败的线程
不可重入没有线程 ID 或重入计数
锁升级/降级允许原子修改写锁位和读锁计数升级失败时线程可能入队
无条件变量不涉及不涉及
非公平调度不阻止新线程插队只管理入队后的顺序
高性能乐观读提供版本戳,无需 CAS不参与

3. 加解锁流程与锁升降级流程

3.1 写锁的获取与释放

详细流程

  1. 线程调用 writeLock()
  2. 检查 (state & ABITS) == 0,即无任何锁。
    • 如果为真,则 CAS 增加 WBIT,成功则返回新 stamp。
    • 如果为假,则进入 acquireWrite
  3. acquireWrite 中:先自旋一定次数尝试获取锁,若仍失败,则创建 WNode 节点(mode=1)加入队列尾部,然后 LockSupport.park() 阻塞。
  4. 当锁被释放(unlockWrite)时,会唤醒队列中的后继节点(通常是头节点的下一个),被唤醒的线程继续尝试获取锁。
  5. 释放时调用 unlockWrite(stamp),将 state 加上 WBIT(翻转写锁位),并唤醒后继。

时序图

sequenceDiagram
    participant T as 线程
    participant L as StampedLock
    T->>L: writeLock()
    alt state & ABITS == 0
        L->>L: CAS(state, state+WBIT)
        L-->>T: 返回 stamp
    else 有锁
        L->>L: acquireWrite() 加入队列 & 自旋/park
        L-->>T: 获得锁后返回 stamp
    end
    T->>T: 写操作
    T->>L: unlockWrite(stamp)
    L->>L: state = stamp + WBIT
    L->>L: 唤醒后继节点

3.2 悲观读锁的获取与释放

详细流程

  1. 线程调用 readLock()
  2. 检查 (state & WBIT) == 0(无写锁)且读锁计数未超限(state + RUNIT < RFULL)。
    • 满足则 CAS 增加 RUNIT,成功返回 stamp。
    • 否则进入 acquireRead
  3. acquireRead 中:如果有写锁,则入队等待;如果只是读锁计数满,则自旋或入队。
  4. 释放时调用 unlockRead(stamp),CAS 减去 RUNIT,若队列中有写锁等待,可能还需要额外唤醒。

时序图

sequenceDiagram
    participant T as 线程
    participant L as StampedLock
    T->>L: readLock()
    alt 无写锁且计数<128
        L->>L: CAS(state, state+RUNIT)
        L-->>T: stamp
    else 有写锁或计数满
        L->>L: acquireRead() 加入队列
        L-->>T: 获得锁后返回 stamp
    end
    T->>T: 读操作
    T->>L: unlockRead(stamp)
    L->>L: CAS(state, state-RUNIT)

3.3 乐观读及验证流程

详细流程

  1. 线程调用 tryOptimisticRead(),返回当前 state 的高位(state & SBITS),或者 0(有写锁时)。
  2. 线程拷贝共享变量到局部变量(无锁保护)。
  3. 调用 validate(stamp)
    • 如果 stamp 为 0,直接返回 false(有写锁)。
    • 否则比较 (stamp & SBITS)(state & SBITS),相等则返回 true,否则 false。
  4. 若验证成功,使用拷贝的数据;若失败,则回退到悲观读锁重新读取。

时序图

sequenceDiagram
    participant T as 线程
    participant L as StampedLock
    T->>L: tryOptimisticRead()
    alt 无写锁
        L-->>T: stamp = state & SBITS
    else 有写锁
        L-->>T: 0
    end
    T->>T: 拷贝共享变量
    T->>L: validate(stamp)
    alt 期间无写锁获取/释放
        L-->>T: true
    else 有写操作
        L-->>T: false
    end

3.4 锁升级(读锁 → 写锁)详细说明

流程

  1. 当前线程已持有读锁(stampR)。
  2. 调用 tryConvertToWriteLock(stampR)
  3. 检查 state 的高位是否匹配(防止 stamp 过期)。
  4. 读取当前低 8 位 m = state & ABITS
    • m == 0(无锁):说明读锁已被释放?实际上不可能,因为高位匹配意味着读锁还在,所以不会进入此分支。
    • m == WBIT:已经是写锁,直接返回原 stamp(幂等)。
    • m == RUNIT:只有一个读锁,则 CAS 将 state 更新为 state - RUNIT + WBIT,成功则返回新 stamp。
  5. 如果失败(例如有其他读锁),返回 0。
  6. 调用者检查返回值:若为 0,则释放原读锁,调用 writeLock() 重新获取写锁;否则使用新 stamp 进行写操作。

时序图

sequenceDiagram
    participant T as 线程
    participant L as StampedLock
    T->>L: 持有读锁 stampR
    T->>L: tryConvertToWriteLock(stampR)
    alt 只有一个读锁且是当前线程
        L->>L: CAS(state, state - RUNIT + WBIT)
        L-->>T: 新写锁 stampW
    else 有其他读锁或条件不满足
        L-->>T: 0
    end
    alt 升级成功
        T->>T: 写操作
        T->>L: unlockWrite(stampW)
    else 升级失败
        T->>L: unlockRead(stampR)
        T->>L: writeLock()
        T->>T: 写操作
        T->>L: unlockWrite(stamp)
    end

3.5 锁降级(写锁 → 读锁)详细说明

流程

  1. 当前线程持有写锁(stampW)。
  2. 调用 tryConvertToReadLock(stampW)
  3. 检查 state 高位匹配。
  4. 若当前低 8 位为 WBIT(写锁),则 CAS 更新为 state - WBIT + RUNIT,返回新读锁 stamp。
  5. 若已经是读锁,直接返回原 stamp(幂等)。
  6. 降级后,当前线程仍然持有锁(读锁),可以继续读取数据。

时序图(省略,与升级类似但方向相反)。


4. 实际应用场景

4.1 场景一:缓存统计计数器(读多写少)

public class CacheStats {
    private long hitCount, missCount;
    private final StampedLock lock = new StampedLock();

    public void recordHit() {
        long stamp = lock.writeLock();
        try {
            hitCount++;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public void recordMiss() {
        long stamp = lock.writeLock();
        try {
            missCount++;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public double getHitRate() {
        long stamp = lock.tryOptimisticRead();
        long localHit = hitCount;
        long localMiss = missCount;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                localHit = hitCount;
                localMiss = missCount;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return localHit + localMiss == 0 ? 0 : (double) localHit / (localHit + localMiss);
    }
}

4.2 场景二:金融产品价格快照(乐观读典型)

public class PriceSnapshot {
    private double bid, ask;
    private final StampedLock sl = new StampedLock();

    public void update(double bid, double ask) {
        long stamp = sl.writeLock();
        try {
            this.bid = bid;
            this.ask = ask;
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    public double getMidPrice() {
        long stamp = sl.tryOptimisticRead();
        double b = bid, a = ask;
        if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
                b = bid;
                a = ask;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        return (b + a) / 2;
    }
}

4.3 场景三:使用锁升级实现条件更新

public class Counter {
    private int value;
    private final StampedLock sl = new StampedLock();

    public void incrementIfLessThan(int threshold) {
        long stamp = sl.readLock();
        try {
            while (value < threshold) {
                long writeStamp = sl.tryConvertToWriteLock(stamp);
                if (writeStamp != 0L) {
                    stamp = writeStamp;
                    value++;
                    break;
                } else {
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
        }
    }
}

5. 吞吐量分析(详细)

5.1 理论开销对比

操作原子操作内存屏障典型耗时(纳秒)
乐观读(tryOptimisticReadvolatile 读~5-10
验证(validatevolatile 读 + loadFence~5-10
悲观读锁(readLock 成功)CASvolatile 读/写~50-100
写锁(writeLock 成功)CASvolatile 读/写~50-100

5.2 基准测试结果(参考 Doug Lea 及社区数据)

测试环境:16 核 CPU,每个操作仅访问内存变量,读:写比例 = 99:1,每个读操作耗时约 10ns(纯内存)。

锁类型吞吐量(百万 ops/s)相对无锁比例
无锁(基线)1200100%
StampedLock(乐观读模式,成功率99%)95079%
StampedLock(强制悲观读)32027%

当写操作比例提高到 10% 时(读:写=90:10):

  • 乐观读成功率降至约 80%,大量回退到悲观读锁,吞吐量降至 220 M ops/s。
  • 强制悲观读模式吞吐量约 280 M ops/s。

当写操作比例进一步提高到 50% 时,乐观读几乎总是失败,导致每次读都要先乐观读再悲观读(额外开销),吞吐量甚至低于强制悲观读模式。

5.3 影响吞吐量的关键因素

  1. 乐观读成功率:取决于写锁的持有频率和持续时间。写锁越频繁、持有时长越大,成功率越低。
  2. 读操作耗时:如果读操作耗时较长(例如涉及复杂计算或 I/O),从乐观读开始到 validate 的时间间隔变大,期间写锁发生的概率增加,失败率上升。
  3. CPU 核数:多核下乐观读的 cache 一致性流量远低于 CAS 操作,因此性能优势更明显。
  4. 锁竞争模式StampedLock 的非公平调度在读多写少时有助于提高吞吐,但极端写多时可能加剧写饥饿。

5.4 调优建议

  • 尽量将读操作设计为简短(仅复制几个字段),避免在乐观读区域内执行耗时操作。
  • 如果乐观读失败率超过 5%,考虑调整业务逻辑或改用悲观读锁模式(直接使用 readLock())。
  • 对于写操作频繁的场景,StampedLock 并不适合,应使用其他并发控制(如 ConcurrentHashMapAtomicLong 等)。

6. 注意事项

6.1 不可重入导致的死锁

绝对不要在持有写锁时再次调用 writeLock(),否则死锁。同样,持有读锁时再次调用 readLock() 虽然会成功(增加读锁计数),但必须配对释放,否则会造成锁泄露。

// 错误示例:死锁
long stamp = lock.writeLock();
lock.writeLock(); // 死锁

6.2 必须手动释放锁

StampedLock 不实现 Lock 接口,没有自动释放机制。每个 lock 方法返回的 stamp 必须保存,并在 finally 块中释放。

long stamp = lock.writeLock();
try {
    // ...
} finally {
    lock.unlockWrite(stamp);
}

6.3 乐观读的数据一致性限制

乐观读不保证读取的多个变量之间的一致性。例如,读取两个共享变量 xy 时,写线程可能在读取 x 后、读取 y 前修改了 xy,导致 validate 虽然成功(因为写锁获取+释放后高位变化,但验证时高位又恢复?实际上写锁每次获取/释放会翻转 WBIT,高位必然改变,所以 validate 会失败)。但更微妙的是:如果写线程只修改了其中一个变量且不释放写锁,则 validate 也会失败。因此,乐观读只能保证读取期间没有完整的写锁周期,但不能保证快照原子性。要保证原子快照,必须使用悲观读锁。

6.4 锁升级失败后的正确处理

tryConvertToWriteLock 返回 0 时,必须释放原读锁,然后重新获取写锁,不能保留读锁再次调用 writeLock()(会导致死锁,因为读锁会阻塞写锁)。

正确模式(见 4.3 场景三)。

6.5 内存可见性保证

乐观读没有自动建立 happens-before 关系,因此必须依赖 validate 中的 loadFence 来保证读操作不会被重排序到验证之后。但仍需注意:tryOptimisticRead 返回后,对共享变量的读取必须发生在 validate 之前,否则可能读到过期数据。

6.6 不支持 Condition

无法使用 await()/signal()。如果需要等待/通知,请使用其他同步机制(如 ReentrantLock + Conditionsynchronized)。

6.7 中断响应

writeLock()readLock() 不支持中断(不可中断)。需要可中断获取时,使用 writeLockInterruptibly() / readLockInterruptibly(),它们会抛出 InterruptedException

6.8 锁泄露风险

因为 stamp 是原始 long 类型,容易在异常流中丢失。务必确保每个锁获取后都有对应的释放,并且不要在不同方法间传递 stamp 而不加保护(例如存储到全局变量)。

6.9 调试与监控

StampedLock 提供了 isWriteLocked()getReadLockCount() 等方法,但返回值是近似值(例如读锁计数可能因并发修改而不准确),仅适用于监控和调试,不能用于业务逻辑决策。

6.10 适用场景总结

场景推荐度原因
读多写少,读操作极短(微秒级)⭐⭐⭐⭐⭐乐观读带来巨大性能提升
读写比例相近或写多⭐⭐乐观读失败率高,退化为悲观读反而增加开销
需要锁重入不可重入,容易死锁
需要 Condition不支持
对内存占用敏感⭐⭐⭐⭐轻量级设计

结束语StampedLock 通过创新的乐观读模式和精巧的 state 位设计,在特定场景下提供了极高的并发性能。但它的使用约束较多,需谨慎设计,避免死锁和锁泄露。理解其内部数据结构(state 位域和 CLH 队列)是正确使用它的关键。