concurrenthashmap 的 putifabsent 方法线程安全,基于 cas 和桶级 synchronized 锁实现“判断+插入”原子性,避免竞态;value 不能为 null,返回 null 表示写入成功,非 null 表示已存在。

ConcurrentHashMap 的 putIfAbsent 本身就是线程安全的,无需额外同步。它底层利用 CAS(Compare-And-Swap)和细粒度锁(JDK 8+ 使用 synchronized 锁住单个 hash 桶),保证“判断是否存在 + 不存在则写入”这一复合操作的原子性。
为什么它能安全应对并发写入
该方法不是简单地先 get 再 put,而是原子执行以下逻辑:
- 计算 key 的 hash 值,定位到对应桶(table[index])
- 在该桶上加锁(或用 CAS 尝试更新头节点)
- 遍历链表或红黑树,检查 key 是否已存在
- 若不存在,则插入新节点;若已存在,直接返回已有 value
- 整个过程对其他线程不可见中间状态
典型安全用法示例
比如缓存用户首次登录时间:
```javaConcurrentHashMap
// 多个线程并发调用,只有第一个成功写入,其余返回已存在的值
Long existing = firstLoginTime.putIfAbsent("user123", System.currentTimeMillis());
if (existing == null) {
// 当前线程是第一个写入者
} else {
// 已有值,说明其他线程已抢先写入
}
```
注意事项
-
value 不能为 null:传入 null 会抛
NullPointerException -
key 和 value 都需可被哈希和比较:确保
hashCode()和equals()正确实现 -
不适用于复杂条件判断:如“仅当旧值满足某条件才更新”,此时应改用
computeIfAbsent或replace等更精确的方法 -
返回值有意义:返回
null表示本次写入成功;返回非null表示键已存在,且返回的是原有值
和普通 HashMap + synchronized 对比
手动加锁模拟 putIfAbsent 容易出错:
- 锁范围过大(如锁整个 map)→ 严重降低并发度
- 锁范围过小(如只锁判断逻辑)→ 无法避免竞态:两个线程同时通过
!containsKey,再同时put,导致覆盖 - ConcurrentHashMap 内部锁粒度精准到桶,兼顾安全性与吞吐量
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











