
什么是读戳升级为写戳
读戳升级为写戳,是指在线程已持有有效读锁(通过 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” 的能力,写锁释放后如需继续读,应重新获取读锁

评论(0)