concurrenthashmap 的 putifabsent 和 remove 各自线程安全,但组合使用不构成原子更新;应优先使用 replace、compute 等内置原子方法实现读-改-写场景。

在 ConcurrentHashMap 中,putIfAbsent 和 remove 本身是线程安全的原子操作,但**组合使用它们并不能天然构成“无锁原子更新”**——关键在于你如何编排逻辑、是否需要强一致性,以及是否涉及读-改-写(read-modify-write)场景。
putIfAbsent 适合“首次写入”场景
putIfAbsent(key, value) 仅在 key 不存在时插入,并返回旧值(null 表示成功插入)。它内部基于 CAS + synchronized 分段锁(JDK8+ 是 CAS + volatile + synchronized 协作),保证单次操作原子性。
- 适用于:缓存初始化、单例注册、幂等任务提交等“只设一次”的逻辑
- 不适用:需要先读出旧值、计算新值、再写入(比如计数器自增)——这会引发 ABA 或丢失更新
- 示例:正确 —— 首次注册用户配置
configMap.putIfAbsent(userId, new UserConfig());
remove(key, value) 实现条件删除
remove(key, value) 仅当 key 存在且当前 value 等于传入的 value 时才删除,也是原子操作。它依赖 value 的 equals 比较,常用于“检查并移除”场景。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 典型用途:取消已提交但尚未执行的任务(需保存任务对象引用)
- 注意:value 必须可被准确比较;若 value 是可变对象,需确保 equals/hashCode 稳定
- 示例:安全 —— 移除指定状态的任务
taskMap.remove(taskId, pendingTask);
组合 putIfAbsent + remove ≠ 原子更新,需谨慎设计
直接链式调用如 map.putIfAbsent(k, v); map.remove(k, oldV); 不具备原子性,中间可能被其他线程修改。若目标是“替换旧值为新值”,应优先使用:
-
replace(key, oldValue, newValue):CAS 式条件替换,原子 -
compute(key, BiFunction)或computeIfPresent:函数式更新,内部加锁粒度小,线程安全 -
merge(key, value, remappingFunction):适合“存在则合并,不存在则设置”逻辑
真正无锁原子更新的推荐写法
对于需要“读取→计算→写入”的场景,避免手动拆解为多个操作。用内置方法更可靠:
- 计数器自增:
map.compute(key, (k, v) -> (v == null ? 0 : v) + 1); - 条件更新(如仅当值 > 100 才设为 0):
map.computeIfPresent(key, (k, v) -> v > 100 ? 0 : v); - 带版本控制的更新(配合 AtomicStampedReference 思路):把 version 封装进 value 对象,用
replace(key, oldPair, newPair)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










