stampedlock 读写转换实战:演示如何将读戳(stamp)在特定条件下原子性地升级为写戳

什么是读戳升级为写戳

读戳升级为写戳,是指在线程已持有有效读锁(通过 tryOptimisticRead() 或 readLock() 获取)的前提下,在确认数据未被修改、且当前确实需要写入时,**不释放读锁、不重新竞争**,直接尝试获取写锁。StampedLock 提供 tryConvertToWriteLock(long stamp) 方法实现这一原子操作。

升级成功的前提条件

该操作能否成功,取决于三个关键点:

传入的 stamp 必须是当前线程之前合法获取的读戳(不能是过期、伪造或来自其他线程的 stamp) 自该读戳获取以来,锁未被其他线程成功执行过写操作(即版本号未变) 当前没有其他线程正在持有写锁(即写锁处于空闲状态)

三者同时满足,tryConvertToWriteLock 才会返回新的有效写戳;否则返回 0,表示升级失败,需按常规流程重试(如先释放读锁,再调用 writeLock())。

典型升级场景与代码结构

常见于“读—判断—写”模式:先乐观读取数据,检查是否满足更新条件,若满足则尝试原地升级为写锁执行修改。例如维护一个带版本号的缓存计数器:

long stamp = sl.tryOptimisticRead();int current = count;if (!sl.validate(stamp)) { // 乐观读失败,降级为悲观读 stamp = sl.readLock(); try { current = count; } finally { sl.unlockRead(stamp); }}// 检查业务条件:仅当 current < 100 才允许递增if (current < 100) { // 尝试将读戳(无论乐观还是悲观)升级为写戳 long writeStamp = sl.tryConvertToWriteLock(stamp); if (writeStamp != 0L) { // 升级成功,直接修改 count++; return true; } else { // 升级失败:说明期间有写操作发生或写锁被占用 // 改用标准写锁流程 writeStamp = sl.writeLock(); try { // 再次校验条件(防止条件失效) if (count < 100) { count++; return true; } } finally { sl.unlockWrite(writeStamp); } }}return false;

注意事项与避坑点

升级不是万能的,使用时需注意:

stamp 必须来源一致:不能把 tryOptimisticRead() 得到的 stamp 传给 tryConvertToWriteLock() 后,又在失败时拿它去调 unlockRead() —— 乐观读戳不可解锁,只能 validate 升级失败后务必重检查业务条件:因为从读到写之间存在时间窗口,原始判断可能已过期(如其他线程刚完成了递增) 悲观读锁 stamp 可用于升级:通过 readLock() 获取的 stamp 调用 tryConvertToWriteLock() 是合法且常用的做法,但需记得后续正确 unlockRead(如果升级失败) 不支持写戳降级为读戳:StampedLock 不提供类似 “downgrade” 的能力,写锁释放后如需继续读,应重新获取读锁

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。