发布于2026-07-09 阅读(0)
扫一扫,手机访问
在并发场景下初始化缓存,ConcurrentHashMap.computeIfAbsent() 可以说是最稳妥的选择之一。它的核心价值在于:原子化地完成“检查-插入”操作,确保同一 key 的初始化逻辑只被第一个到达的线程执行一次,其余线程阻塞等待结果,从而避免经典 if-put 方式带来的竞态条件和重复初始化问题。

简单来说,ConcurrentHashMap.computeIfAbsent() 能保证:当多个线程同时往同一个 key 上塞值时,最多只有一个线程会执行初始化逻辑(也就是 mappingFunction),其他线程会等着,直到拿到这个唯一的结果。
先看一段常见但存在问题的代码:
❌ 不安全(竞态条件)
if (map.get(key) == null) {
map.put(key, createExpensiveValue()); // 多个线程可能同时进入
}
这段代码的问题在于竞态条件:线程 A 检查到 key 不存在,还没来得及 put,线程 B 也跑过来检查,发现也是 null。结果两个线程都调用了 createExpensiveValue(),不仅造成重复初始化、资源浪费,严重时还可能引发逻辑错误。
而 computeIfAbsent 将“检查”和“插入”这两个步骤合并为一个原子操作,内部加锁粒度控制在 hash 桶级别,既保证了性能,又做到了语义上的严谨。
基本用法十分直接:
ConcurrentHashMap cache = new ConcurrentHashMap();
ExpensiveObject obj = cache.computeIfAbsent("key1", k -> new ExpensiveObject(k));
这里有几个关键点值得注意:
以下是一些容易踩坑的地方,需要特别注意:
为提升代码的可读性和复用性,可以将初始化逻辑单独抽取出来:
private final ConcurrentHashMap cache = new ConcurrentHashMap();
private ExpensiveObject getOrCreate(String key) {
return cache.computeIfAbsent(key, this::createExpensiveObject);
}
private ExpensiveObject createExpensiveObject(String key) {
// 可加入日志、指标、重试等逻辑
return new ExpensiveObject(key);
}
这种拆分好处很多:业务逻辑更清晰,也方便对 createExpensiveObject 方法进行单元测试。同时,如果未来需要调整初始化策略(比如增加超时、重试或者监控),改动起来也相当方便。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8