是否存在可重入的StampedLock实现?支持已持写锁时复用获取读锁
可重入StampedLock实现方案
JDK原生的StampedLock本身不支持重入,这是其设计时为追求极致性能做出的取舍——去掉重入逻辑能大幅降低锁的运行开销。针对你提出的「持有写锁时调用读锁方法直接复用写锁」的需求,有两种可行的解决思路:
一、自定义封装可重入StampedLock
你可以基于原生StampedLock,结合ThreadLocal跟踪当前线程的锁持有状态,实现符合需求的可重入逻辑:
- 核心逻辑:获取读锁前,先检查当前线程是否持有有效写锁,若是则直接复用写锁的stamp(写锁天然具备读权限);若已持有读锁则增加重入计数;否则走正常读锁获取流程。
- 写锁的重入同理:若当前线程已持有有效写锁,直接返回原stamp即可,无需重新加锁。
以下是一个简化的实现示例:
public class ReentrantStampedLock { private final StampedLock delegate = new StampedLock(); // 跟踪当前线程持有的写锁stamp(0表示未持有) private final ThreadLocal<Long> currentWriteStamp = ThreadLocal.withInitial(() -> 0L); // 跟踪当前线程持有的读锁重入次数 private final ThreadLocal<Integer> readLockCount = ThreadLocal.withInitial(() -> 0); // 可重入写锁获取 public long writeLock() { long stamp = currentWriteStamp.get(); if (stamp != 0 && delegate.validate(stamp)) { // 已持有有效写锁,直接重入 return stamp; } // 未持有写锁,获取新锁 stamp = delegate.writeLock(); currentWriteStamp.set(stamp); return stamp; } // 智能读锁获取:持有写锁时直接复用 public long readLock() { // 先检查是否持有写锁 long writeStamp = currentWriteStamp.get(); if (writeStamp != 0 && delegate.validate(writeStamp)) { return writeStamp; } // 检查是否持有读锁 int count = readLockCount.get(); if (count > 0) { readLockCount.set(count + 1); // 返回有效读锁stamp(简化处理,实际可缓存当前读stamp) return delegate.readLock(); } // 首次获取读锁 long stamp = delegate.readLock(); readLockCount.set(1); return stamp; } // 通用解锁方法 public void unlock(long stamp) { if (delegate.isWriteLockStamp(stamp)) { if (currentWriteStamp.get().equals(stamp)) { currentWriteStamp.set(0L); delegate.unlockWrite(stamp); } } else if (delegate.isReadLockStamp(stamp)) { int count = readLockCount.get(); if (count > 1) { readLockCount.set(count - 1); } else { readLockCount.set(0); delegate.unlockRead(stamp); } } } // 可扩展tryLock、乐观读等方法,需遵循相同重入逻辑 }
注意:自定义封装需要仔细处理锁验证、重入计数维护、解锁逻辑的正确性,否则容易出现死锁或锁泄漏问题,建议生产环境使用前做充分测试。
二、替代方案:使用ReentrantReadWriteLock
如果你的场景对性能要求没有极致到必须用StampedLock,JDK原生的ReentrantReadWriteLock是更稳妥的选择:
- 它天然支持可重入,且持有写锁的线程可以直接获取读锁(写锁的独占性允许当前线程重入读锁),完全符合你的需求。
- 实现简单、稳定,无需自行封装复杂逻辑。
示例代码:
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private final Lock readLock = rwLock.readLock(); private final Lock writeLock = rwLock.writeLock(); public void writeOperation() { writeLock.lock(); try { // 执行写操作 readOperation(); // 当前线程持有写锁,可直接获取读锁 } finally { writeLock.unlock(); } } public void readOperation() { readLock.lock(); try { // 执行读操作 } finally { readLock.unlock(); } }
缺点:
ReentrantReadWriteLock的读锁是悲观锁,高并发读场景下性能不如StampedLock的乐观读模式,但胜在原生支持、维护成本低。
目前业界没有被广泛认可的开源可重入StampedLock实现,因为StampedLock的设计初衷就是通过牺牲重入性来换取性能优势。你可以根据自身场景的性能需求,选择自定义封装或使用ReentrantReadWriteLock替代。
内容的提问来源于stack exchange,提问作者Gili
相关产品推荐
相关产品推荐

