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

JMM重排序与缓存同步场景下,JCStress测试为何未出现-1结果?

JCStress测试中currentMap.get未返回null的原因

测试代码

public class Cache_01_Sample {

    @JCStressTest
    @Outcome(expect = ACCEPTABLE, desc = "9")
    @Outcome(id = "-1", expect = FORBIDDEN, desc = "0")
    @State
    public static class DemoCache {
        private Map<Integer, Integer> currentMap = new HashMap<>();

        private int size = 100000;

        public DemoCache() {
            for (int i = 0; i < size; i++) {
                currentMap.put(i, i);
            }
        }

        @Actor
        public void get(I_Result result) {
            int i = new Random().nextInt(0, size);
            Integer value = this.currentMap.get(i);
            if (null == value) {
                result.r1 = -1;
            } else {
                result.r1 = value;
            }
        }

        @Actor
        public void refresh() {
            Map<Integer, Integer> map = new HashMap<>();
            for (int i = 0; i < size; i++) {
                map.put(i, i);
            }

            currentMap = map;
        }
    }
}

问题背景

基于JMM重排序与缓存同步机制开展上述JCStress测试,结果显示-1(即currentMap.get返回null的情况)从未出现,希望了解该现象的原因。

原因分析

  • 单线程内的执行顺序保证:refresh方法中,新HashMap的所有put操作必然在currentMap = map的引用赋值之前完成。JMM的as-if-serial语义确保单个线程内的操作会按照代码逻辑顺序执行,不会出现“先把空map赋值给currentMap,再往里面put数据”的重排序。因此,当其他线程看到currentMap指向新map时,这个新map已经是完整初始化的,包含了0到99999的所有键值对。
  • 旧map的完整性:即使get线程还在访问旧的currentMap,旧map在构造方法中已经完成了所有键值对的初始化,随机生成的i(0到99999)必然是旧map中存在的键,因此get操作不会返回null。
  • 无半初始化map被访问:currentMap的引用赋值是原子操作,线程要么看到旧的完整map,要么看到新的完整map,不存在访问“正在初始化的半完整map”的可能。

综上,无论get线程访问的是旧map还是新map,目标键都必然存在,因此currentMap.get永远不会返回null,测试中也就不会出现-1的结果。

内容的提问来源于stack exchange,提问作者刘警君

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:07:10