ConcurrentHashMap 并非完全线程安全,单个方法调用(如 put、get、remove、computeIfAbsent、merge)原子,但复合操作需额外同步;size() 返回近似值,keySet().iterator() 为弱一致性迭代器。

ConcurrentHashMap 真的完全线程安全吗?
它不是“所有操作都加全局锁”的线程安全,而是通过分段锁(JDK 7)或 CAS + synchronized(JDK 8+)实现**部分操作的线程安全**。关键在于:单个方法调用是原子的,但复合操作(如 if (map.get(k) == null) map.put(k, v))仍需额外同步。
哪些操作是原子的?哪些不是?
以下方法在 JDK 8+ 中保证单次调用的原子性:put、get、remove、computeIfAbsent、merge。但这些不是:
-
size()返回的是近似值(可能滞后),高并发下不建议用于条件判断 -
keySet().iterator()返回的迭代器是弱一致性(weakly consistent),可遍历到已删除元素,也可能跳过刚插入元素 -
for (Entry e : map.entrySet())同样基于弱一致性迭代器,不保证看到全部实时变更
为什么不能用 ConcurrentHashMap 替代 synchronized 块?
常见误用是把 ConcurrentHashMap 当作“免同步万能容器”。例如:
if (!map.containsKey(key)) {
map.put(key, expensiveCompute());
}
这仍是竞态条件:两个线程同时通过 containsKey 检查,都会执行 expensiveCompute() 并写入。正确写法是:
ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有
立即学习“Java免费学习笔记(深入)”;
map.computeIfAbsent(key, k -> expensiveCompute());
该方法内部用 CAS 或细粒度锁确保只计算一次。其他类似场景也应优先使用 computeIfPresent、merge 等原子更新方法。
JDK 7 和 JDK 8+ 的底层差异影响什么?
升级后行为变化容易被忽略:
- JDK 7 使用
Segment数组分段加锁,concurrencyLevel影响初始化桶数,但实际并发度由哈希分布决定 - JDK 8+ 废除
Segment,改用Node数组 + 链表/红黑树,锁粒度细化到单个Node(即数组槽位) -
put在 JDK 8+ 中对空槽位用 CAS,非空时再加synchronized锁住首节点;这意味着高冲突哈希可能导致局部锁争用 -
size()在 JDK 8+ 中通过累加每个桶的修改计数(baseCount + sum of counterCells)估算,但不保证强一致
put,而是你写的那几行看似“顺手”的 if-else 逻辑。









