ConcurrentHashMap 仅保证单个操作线程安全,组合操作需用 computeIfAbsent 等原子方法;迭代器弱一致,避免遍历时修改;合理设 initialCapacity;value 需自身线程安全或封装于 compute 操作中。

ConcurrentHashMap 是 Java 并发编程中最常用、也最容易误用的集合之一。写好它,关键不是“怎么初始化”,而是“怎么避免隐式同步、竞态和非原子操作”。高质量使用的核心是:理解其线程安全边界、避开非原子组合操作、合理选择方法语义。
明确 ConcurrentHashMap 的线程安全范围
ConcurrentHashMap 保证单个操作(如 put、get、remove、computeIfAbsent)是线程安全的,但多个操作组合不是原子的。例如下面代码是危险的:
❌ 错误示范(竞态条件):
if (!map.containsKey("key")) {
map.put("key", "value"); // 可能多个线程同时进入 if,重复 put
}
✅ 正确做法:用原子方法替代条件判断+操作
立即学习“Java免费学习笔记(深入)”;
- 用 computeIfAbsent("key", k -> "value") 替代 if + put —— 它内部一次完成“查+算+存”,且整个过程线程安全
- 用 putIfAbsent("key", "value") 替代 if + put —— 若 key 不存在才插入,返回旧值或 null
- 用 replace("key", oldValue, newValue) 实现带条件的更新,避免 get + compare + put 三步拆分
慎用迭代器和批量视图(keySet / values / entrySet)
ConcurrentHashMap 的迭代器是弱一致性(weakly consistent)的:不抛 ConcurrentModificationException,可反映部分更新,但不保证看到所有变化,也不保证顺序。因此:
- 不要在遍历 entrySet() 时调用 remove() 或 put() —— 虽不崩溃,但行为不可预测
- 需要强一致性遍历时,应先用 new HashMap(map) 快照一份(适合读多写少、数据量可控场景)
- 统计类操作优先用内置聚合方法:mappingCount()(比 size() 更准确,不受扩容影响)、reduceValues()、searchKeys() 等
合理设置初始容量与并发级别(Java 8+ 已简化)
Java 8 起,并发段(segments)被取消,底层改用 CAS + synchronized 锁单个 bin(哈希桶),所以 concurrencyLevel 参数仅作为扩容参考,不再控制锁粒度。但仍建议:
- 预估元素数量,设置合适的 initialCapacity(默认 16),避免频繁扩容(扩容是同步且耗时的)
- 公式参考:initialCapacity = (expectedSize / 0.75f) + 1(0.75 是默认负载因子)
- 除非极特殊场景(如明确知道写操作集中在少数 key),否则无需手动指定 concurrencyLevel
避免将非线程安全对象作为 value 存储
ConcurrentHashMap 保护的是对 Map 结构的操作安全,不保护 value 对象内部状态。例如:
ConcurrentHashMap<String, ArrayList<String>> map = new ConcurrentHashMap<>();
map.computeIfAbsent("list", k -> new ArrayList<>()).add("item"); // ✅ 安全:add 在 compute 内部执行
// 但以下不安全:
ArrayList<String> list = map.get("list");
list.add("item"); // ❌ 危险:多个线程 get 到同一个 list 后并发 add,导致数据错乱
解决方式:
- value 使用不可变对象(String、Integer、LocalDateTime 等)
- 若必须用可变容器,选用线程安全实现:如 CopyOnWriteArrayList(适合读远多于写的场景)、Vector(已过时,不推荐)、或封装成带锁的自定义容器
- 更推荐:把复合逻辑封装进 computeXXX 方法中,让操作封闭在 ConcurrentHashMap 的原子上下文中


















