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 反馈 — 无需本地设置。
此课程中的所有课时
- ReentrantLock 与 synchronized
- 用于读写场景的 ReadWriteLock
- 原子变量:无锁更新
- StampedLock 与乐观读取