concurrenthashmap 的 computeifabsent 能保证“对同一个 key,映射函数最多执行一次”,核心靠细粒度桶锁 + cas 标记 reserved 状态协同控制,避免全局锁与先查后 put 问题;不同 key 并发无干扰,同 key 被桶锁串行化,首个线程 cas 占位后计算并写入,异常时 jdk 9+ 自动清理状态,jdk 8 可能残留阻塞;需避免死锁、确保 mappingfunction 纯函数且不返回 null。

ConcurrentHashMap 的 computeIfAbsent 能保证“对同一个 key,映射函数最多执行一次”,核心靠的是底层细粒度锁 + CAS 机制协同控制,不是靠全局锁,也不是简单地“先查后 put”。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
锁粒度只落在对应哈希桶上
它不会锁整个 map,而是定位到 key 所在的哈希桶(Node 数组中的某个 bin),对该桶加锁(JDK 8+ 是用 synchronized 锁住首节点,或通过 CAS 配合 volatile 状态字段)。这意味着: - 不同 key 落在不同桶,完全并发执行,互不影响 - 同一个 key 的多次调用,会被串行化到同一个桶锁下,自然避免竞争
内部用 RESERVED 状态标记“正在计算”
当首个线程发现 key 不存在,会先尝试用 CAS 将该位置设为一个特殊占位节点(ReservationNode 或类似 RESERVED 状态),表示“此 key 正在初始化”。后续线程看到这个状态,就不再触发计算,而是等待并复用结果。这个状态切换是原子的,且对其他线程可见。
整个过程是“检查 → 占位 → 计算 → 写入 → 通知”的闭环
不是原子的“读-算-写”三步整体不可中断,但关键环节受控: - 检查 key 是否存在是快速、无锁的(基于 volatile 读) - 占位操作用 CAS 保证唯一性 - 计算阶段由单一线程执行,不允许多个线程进入 - 写入结果前清除 RESERVED 状态,唤醒等待线程 - 若 mappingFunction 抛异常,JDK 9+ 会自动清理 RESERVED 状态,避免阻塞;JDK 8 则可能残留,导致后续调用一直等待
必须配合正确用法才能发挥效果
原子性保障只对方法自身逻辑有效,不兜底错误写法:
- 不能在 mappingFunction 里再调 computeIfAbsent 或 put 同一个 map,否则可能死锁
- mappingFunction 必须是纯函数:不依赖也不修改外部可变状态,不返回 null(否则 value 为 null,看似“没缓存”,实则已跳过后续计算)
- 不能用非线程安全的 Map(如 HashMap)调用 computeIfAbsent,它的原子性在非并发容器中不成立
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










