concurrentmap 通过 jmm 规范保证内存可见性与操作原子性,提供 putifabsent、remove、replace 等原生原子方法,并非所有继承自 map 的方法都线程安全。

Map 接口定义了最基本的键值映射行为,而 ConcurrentMap 在其基础上明确引入线程安全语义与原子操作契约——它不只是“能并发用的 Map”,而是通过方法签名和 JMM(Java 内存模型)规范,保证特定操作的不可分割性与内存可见性。
语义扩展:从行为契约到内存一致性承诺
Map 接口只规定功能逻辑(如 put 覆盖旧值、get 返回当前值),不承诺多线程下的行为。ConcurrentMap 则在接口注释中明确定义内存一致性效果:向 ConcurrentMap 存入对象作为 key 或 value 的动作,happens-before 于其他线程后续对该对象的读取或删除。这意味着:
- 无需额外同步,就能确保写入的 key/value 对其他线程可见
- 避免因指令重排或缓存不一致导致的“部分初始化”问题
- 这种 happens-before 关系是接口层级的契约,所有实现(如 ConcurrentHashMap)必须遵守
原子操作增强:消除竞态的原生支持
ConcurrentMap 显式提供一批“检查-执行”一体化方法,把原本需手动加锁+判断的逻辑封装为单次调用。这些方法不是语法糖,而是底层由 CAS、volatile 写或细粒度锁保障的真正原子性:
- putIfAbsent(k, v):仅当 key 不存在时插入,返回旧值或 null;不会覆盖已有映射
- remove(k, v):仅当 key 存在且对应值等于 v 时才删除,防止误删并发修改后的值
- replace(k, oldV, newV):仅当 key 存在且当前值等于 oldV 时更新为 newV,适用于状态机切换场景
- compute、merge 等默认方法:基于当前值做函数式计算,整个读-算-写过程对指定 key 是原子的
继承关系不等于能力平移
ConcurrentMap 继承自 Map,但并非所有 Map 方法在并发环境下都安全。例如:
- containsKey + get 组合不是原子的,即使用于 ConcurrentMap,中间仍可能被其他线程修改
- size() 在高并发下可能返回近似值(ConcurrentHashMap 实现中),不保证强一致性
- entrySet().iterator() 不支持并发修改,遍历时若发生写操作仍会抛 ConcurrentModificationException(fail-fast)
真正获得并发安全,依赖的是 ConcurrentMap 自身定义的原子方法,而非继承来的通用方法。
使用建议:何时选 ConcurrentMap 而非 synchronized(Map)
直接包装 HashMap 并用 synchronized 控制访问,虽能保证线程安全,但存在明显短板:
- 整个 map 被一把锁保护,读写相互阻塞,吞吐量随线程数增加急剧下降
- 无法表达“仅当满足条件才更新”这类业务逻辑,必须自行加锁+判断,易出错
- 缺乏内存模型层面的可见性保障,需额外 volatile 或 synchronized 配合
- ConcurrentMap 实现(如 ConcurrentHashMap)采用分段/桶级锁或无锁算法,在读多写少场景下性能优势显著
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











