You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

是否存在可重入的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 03:40:29