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

ReentrantLock锁块内多次volatile读取可否替换为单次本地变量读取

结论

你完全可以将references字段单次读取到本地变量缓存,这种改写不仅不会违反JMM规范,还能减少volatile读带来的内存屏障开销,提升执行效率。


原因分析

  • 你代码中的所有读写操作都发生在lock()成功后的临界区内:Lock接口的内存语义保证,首次成功的lock操作已经完成了完整的内存同步,你读到的references值一定是全局最新的,完全符合可见性要求。
  • 你担心的「可重入加解锁无需提供内存同步效果」的规则对这个场景没有影响:如果是重入加锁,说明当前线程已经持有该Segment的锁,首次加锁时已经完成了内存同步,当前线程对references的可见性本来就有保障;而且持有锁期间没有其他线程能进入临界区修改references,你本地缓存的数组引用在整个临界区执行过程中都是稳定有效的。
  • 你提到的「未成功加解锁无同步效果」的规则只适用于加锁失败的场景(比如tryLock返回false),而你的代码是在lock()成功后才进入的try块,完全不受这个规则约束。

改写示例

你可以参考如下改写逻辑,功能和原代码完全等价:

protected final class Segment extends ReentrantLock {

    private volatile Reference<K, V>[] references;

    private void restructure(boolean allowResize, @Nullable Reference<K, V> ref) {
        boolean needsResize;
        lock();
        try {
            // 仅一次volatile读,缓存到本地变量
            Reference<K, V>[] refs = this.references;
            boolean resizing = false;
            int restructureSize = refs.length;
            //...
            Reference<K, V>[] restructured = 
                    resizing ? createReferenceArray(restructureSize) : refs;

            for (int i = 0; i < refs.length; i++) {
                ref = refs[i];
                //...
            }

            if (resizing) {
                this.references = restructured;
                // 直接用新的数组对象计算阈值,不需要再读volatile字段
                this.resizeThreshold = (int) (restructured.length * getLoadFactor());
            }
            //...
        } finally {
            unlock();
        }
    }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 15:09:00