0Pricing
Java Academy · 课时

StampedLock 与乐观读取

在读操作占主导且吞吐量要求较高的工作负载中,应用 StampedLock 的乐观读取模式

StampedLock 与乐观读取 是 CoddyKit 上的免费 Java Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

什么是 StampedLock

StampedLock 在 Java 8 中引入,在 ReadWriteLock 概念的基础上增加了乐观读取模式。操作会返回一个 long 类型的标记,用于释放锁或验证乐观读取。

import java.util.concurrent.locks.*;

StampedLock sl = new StampedLock();

// Exclusive write:
long stamp = sl.writeLock();
try {
    // write data
} finally {
    sl.unlockWrite(stamp);
}

// Shared read:
stamp = sl.readLock();
try {
    // read data
} finally {
    sl.unlockRead(stamp);
}

乐观读取模式

乐观读取不会获取锁——它只获取一个标记。读取后,请验证该标记。如果验证失败(期间有写入者介入),则回退到真正的读锁:

StampedLock sl2 = new StampedLock();
double x = 0, y = 0;

// Try optimistic read first:
long stamp = sl2.tryOptimisticRead();
double localX = x, localY = y; // read values

if (!sl2.validate(stamp)) {
    // A write occurred — fall back to read lock
    stamp = sl2.readLock();
    try {
        localX = x;
        localY = y;
    } finally {
        sl2.unlockRead(stamp);
    }
}
// use localX and localY

完整的二维 Point 示例

经典的 StampedLock 示例——使用乐观读取的二维点:

class Point {
    private double x, y;
    private final StampedLock lock = new StampedLock();
    
    void move(double dx, double dy) {
        long stamp = lock.writeLock();
        try { x += dx; y += dy; }
        finally { lock.unlockWrite(stamp); }
    }
    
    double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();
        double cx = x, cy = y;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try { cx = x; cy = y; }
            finally { lock.unlockRead(stamp); }
        }
        return Math.sqrt(cx * cx + cy * cy);
    }
}

锁转换

StampedLock 支持在不同锁模式之间进行转换:

StampedLock sl3 = new StampedLock();

// Read → Write upgrade attempt:
long readStamp = sl3.readLock();
long writeStamp = sl3.tryConvertToWriteLock(readStamp);
if (writeStamp != 0) {
    // Successfully upgraded!
    // ... write ...
    sl3.unlockWrite(writeStamp);
} else {
    // Upgrade failed — release read and reacquire write
    sl3.unlockRead(readStamp);
    writeStamp = sl3.writeLock();
    // ... write ...
    sl3.unlockWrite(writeStamp);
}

乐观读取最有帮助的场景

乐观读取在以下情况下最有效:

  • 读取频率远高于写入频率
  • 数据快照可以存放在少量局部变量中(无需分配对象)
  • 写入竞争较低(验证很少失败)

如果写入频繁,乐观读取会反复回退到完整的读锁,从而无法带来任何收益。

StampedLock 与 ReadWriteLock

主要区别:

  • StampedLock:增加乐观读取;NOT 可重入;不支持条件变量
  • ReentrantReadWriteLock:可重入;支持条件变量;没有乐观模式

在写入竞争较低时,StampedLock 更快。ReentrantReadWriteLock 更安全,功能也更丰富。

StampedLock 是 NOT 可重入的

与 ReentrantLock 不同,StampedLock 是 NOT 可重入的。尝试获取自己已经持有的锁模式的线程会发生死锁:

StampedLock sl4 = new StampedLock();
long s1 = sl4.readLock();
// DO NOT: sl4.readLock() again — deadlocks with non-reentrant!

tryWriteLock 与 tryReadLock

非阻塞获取尝试在失败时返回 0:

StampedLock sl5 = new StampedLock();

long stamp = sl5.tryWriteLock();
if (stamp != 0) {
    try {
        System.out.println("Got write lock");
    } finally {
        sl5.unlockWrite(stamp);
    }
} else {
    System.out.println("Write lock unavailable");
}

作为 ReadWriteLock 使用

StampedLock 可以返回一个标准 ReadWriteLock 视图,以实现兼容:

StampedLock sl6 = new StampedLock();
Lock readLock  = sl6.asReadLock();
Lock writeLock = sl6.asWriteLock();
ReadWriteLock rwView = sl6.asReadWriteLock();
// These provide standard Lock interface without optimistic mode

性能基准直觉

基准测试结果通常显示:

  • 读取占绝大多数(99% 读取):StampedLock > ReadWriteLock > 同步机制
  • 混合工作负载(70% 读取):ReadWriteLock ≈ StampedLock
  • 写入占主导:同步机制或 ReentrantLock 通常更有优势(开销更低)

在优化之前,请始终使用符合实际情况的工作负载进行性能分析。

解锁后标记失效

使用标记释放锁后,该标记就会失效。解锁后使用旧标记会导致未定义行为——请始终为每次获取锁保存一个新的标记。

StampedLock sl7 = new StampedLock();
long stamp = sl7.writeLock();
sl7.unlockWrite(stamp);
// stamp is now invalid — don't use it:
// sl7.unlockWrite(stamp); // throws IllegalMonitorStateException or corrupts state

快速检查

使用 tryOptimisticRead() 进行乐观读取,随后 validate(stamp) 返回 false。代码接下来应该做什么?

回顾:StampedLock

关键要点:

  • 三种模式:乐观读取(无锁)、读锁和写锁
  • tryOptimisticRead() + validate() — 零开销的读取尝试
  • 验证失败时 → 回退到 readLock()
  • NOT 可重入——同一线程再次获取锁时存在死锁风险
  • 不支持条件变量——需要时请使用 ReentrantReadWriteLock

常见问题解答

「StampedLock 与乐观读取」课时是免费的吗?

是的 — 「StampedLock 与乐观读取」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「StampedLock 与乐观读取」这节课中我会学到什么?

在读操作占主导且吞吐量要求较高的工作负载中,应用 StampedLock 的乐观读取模式 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「StampedLock 与乐观读取」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. ReentrantLock 与 synchronized
  2. 用于读写场景的 ReadWriteLock
  3. 原子变量:无锁更新
  4. StampedLock 与乐观读取
← 返回 Java Academy