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

基于StampedLock实现缓存读写加载方法,如何结合官方示例优化?

嘿,我刚好之前也折腾过StampedLock的这种混合场景,结合乐观读和读升级写锁来做缓存加载确实是读多写少场景下的高效方案,完全适配Doug Lea给出的Javadoc示例思路。我帮你把代码补全并优化到符合最佳实践,顺便拆解下关键逻辑:

完整实现代码
public class StampedLockCacheExample {
    private final StampedLock lock = new StampedLock();
    private final Map<String, String> cached = new HashMap<>();

    public String getConverted(String input) {
        String customised = null;
        long stamp = 0L;

        while (customised == null) {
            // 验证乐观读戳记有效性,无效则重新获取乐观读权限
            if (!lock.validate(stamp)) {
                stamp = lock.tryOptimisticRead();
                // 乐观读模式下读取缓存
                customised = cached.get(input);
                
                // 缓存命中时再次验证戳记,防止读取后有写操作篡改缓存
                if (customised != null) {
                    if (lock.validate(stamp)) {
                        return customised;
                    }
                    // 戳记失效,缓存可能已过期,重置状态重新循环
                    customised = null;
                }
            } else {
                // 戳记有效,直接读取缓存
                customised = cached.get(input);
                if (customised != null) {
                    return customised;
                }
            }

            // 缓存未命中,先获取读锁再二次检查
            stamp = lock.readLock();
            try {
                // 再次检查缓存:避免其他线程在我们获取读锁前已加载数据
                customised = cached.get(input);
                if (customised == null) {
                    // 尝试将读锁升级为写锁
                    long writeStamp = lock.tryConvertToWriteLock(stamp);
                    if (writeStamp != 0L) {
                        // 升级成功,持有写锁
                        stamp = writeStamp;
                        // 从数据源加载实际数据(替换为你的业务逻辑)
                        customised = loadFromSource(input);
                        // 更新缓存
                        cached.put(input, customised);
                    } else {
                        // 升级失败,释放读锁后重新循环重试
                        lock.unlockRead(stamp);
                        stamp = 0L;
                        customised = null;
                    }
                }
            } finally {
                // 仅在持有读锁时释放,避免误释放写锁
                if (lock.isReadLockStamp(stamp)) {
                    lock.unlockRead(stamp);
                }
            }
        }

        // 最后释放写锁(如果持有)
        if (lock.isWriteLockStamp(stamp)) {
            lock.unlockWrite(stamp);
        }
        return customised;
    }

    // 模拟从数据库/外部接口加载数据的方法
    private String loadFromSource(String input) {
        // 这里替换为你的实际业务加载逻辑
        return "processed_" + input;
    }
}
关键逻辑拆解
  • 乐观读优先:先尝试无锁的乐观读,通过tryOptimisticRead()获取戳记,读取缓存后立即用validate(stamp)验证——如果期间没有写操作,直接返回缓存值,这是读多写少场景下性能最高的路径。
  • 缓存二次检查:缓存未命中时先加读锁再检查缓存,避免多个线程同时触发数据加载,这是防止重复加载的关键。
  • 读锁平滑升级:用tryConvertToWriteLock(stamp)尝试升级写锁,成功则加载数据更新缓存;失败则释放读锁重新循环,避免阻塞其他读操作。
  • 循环重试机制:整个逻辑包裹在while循环中,处理乐观读戳记失效、写锁升级失败等异常情况,确保最终能获取到正确的缓存值。
注意事项
  • 永远不要跳过validate(stamp)的检查,乐观读的正确性完全依赖于这个验证步骤。
  • 锁的释放要区分读锁和写锁,避免漏释放或错误释放导致死锁。
  • loadFromSource方法要保证幂等性,因为可能存在多个线程重试加载的情况,重复调用不能产生副作用。

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:47:38