concurrenthashmap 的原子操作(如 computeifabsent、compute、merge、replace、remove)可避免并发写入覆盖,因其内置线程安全机制确保“读-改-写”全过程原子性,无需外部同步。

ConcurrentHashMap 的原子操作能有效避免并发写入时的覆盖问题,关键在于不依赖外部同步,而是利用其内置的线程安全方法直接完成“读-改-写”全过程。
用 computeIfAbsent 做线程安全的懒初始化
当需要为某个 key 创建新值(如构建一个集合、初始化对象),且多个线程可能同时触发时,computeIfAbsent 保证只执行一次工厂逻辑,其他线程直接获取结果。
例如统计每个用户访问的 URL 列表:
ConcurrentHashMap<string list>> userUrls = new ConcurrentHashMap();
// 多个线程同时调用,不会重复 new ArrayList()
userUrls.computeIfAbsent("user123", k -> new ArrayList()).add("/home");</string>
注意:lambda 中不能有副作用,且返回值不能为 null。
用 compute 或 merge 实现安全累加或合并
当需对已有值做更新(如计数、拼接字符串、合并集合),compute 和 merge 可以在单次调用中完成“读取旧值 → 计算新值 → 写入”三步,全程原子。
- compute:适合通用逻辑,接收 key 和旧值(可能为 null),返回新值(null 表示删除)
- merge:更简洁,适用于“有旧值则用 remappingFunction 合并,无旧值则直接 put”
计数示例:
ConcurrentHashMap<string integer> counts = new ConcurrentHashMap();
// 安全 +1,即使并发调用也不会丢失更新
counts.compute("request", (k, v) -> (v == null) ? 1 : v + 1);
// 或用 merge(更直观)
counts.merge("request", 1, Integer::sum);</string>
避免先 get 再 put 的非原子陷阱
下面写法是错误的,存在竞态条件:
// ❌ 危险!中间可能被其他线程修改 Integer old = map.get(key); map.put(key, old == null ? 1 : old + 1); // 覆盖风险高
因为 get 和 put 是两个独立操作,期间其他线程可能已更新该 key,导致结果被覆盖。必须用 compute/merge/computeIfAbsent 等原子方法替代。
结合 CAS 思路理解 replace 和 remove 的条件更新
replace(key, oldValue, newValue) 和 remove(key, value) 提供基于期望值的更新,类似乐观锁:
- 仅当当前值等于 oldValue 时才替换,否则失败并返回 false
- 适合需要“检查状态再变更”的场景,比如状态机流转、库存扣减
示例(仅当订单状态为 “pending” 才更新为 “processed”):
ConcurrentHashMap<string string> orderStatus = new ConcurrentHashMap();
orderStatus.put("ORD-001", "pending");
// 成功返回 true,失败返回 false,不会意外覆盖
boolean success = orderStatus.replace("ORD-001", "pending", "processed");</string>
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











