concurrenthashmap 通过细粒度桶级锁与 cas 乐观机制协同实现高并发:java 8+ 废除 segment,写操作先 cas 尝试,失败再锁单桶头节点;扩容支持多线程协作;避免在 computeifabsent 等中执行耗时逻辑,size() 应换为 mappingcount()。

ConcurrentHashMap 通过细粒度加锁 + 无锁化读写协同,把高并发写操作的锁竞争压到最低——它不锁整个表,也不锁整个段,只在真正发生冲突时,才对单个桶(链表头或红黑树根)加锁。
锁粒度降到单个桶节点
Java 8+ 版本彻底取消了 Segment 分段锁,改用数组 + Node 链表/红黑树结构。写操作(如 put)流程如下:
- 先根据 key 的 hash 定位到具体桶(数组下标)
- 若桶为空,直接 CAS 插入新节点——全程无锁
- 若桶非空(已有节点),则对该桶的头节点(Node 或 TreeBin)加 synchronized 锁
- 锁内完成插入、链表转树、扩容检查等逻辑,其他桶完全不受影响
CAS 优先,失败才升级为锁
大多数无竞争场景下,put、remove 等操作靠 CAS 就能完成,避免进入临界区:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 初始化桶、设置首节点、更新计数器(baseCount)都优先尝试 CAS
- 只有 CAS 失败(比如多个线程同时往同一桶写),才会退回到 synchronized 块中串行处理
- 这种“乐观尝试 + 悲观兜底”策略,让低冲突场景吞吐量接近 HashMap
扩容也支持并发协作
扩容不再是独占式阻塞操作,而是多线程可协助迁移:
- 首个触发扩容的线程负责初始化新表,并标记迁移进度
- 后续线程若访问到正在迁移的桶,会主动帮着把旧桶数据搬进新表
- 读操作可无缝转发到新表,写操作在迁移中也能继续落库(锁对应桶即可)
避免人为放大锁竞争
即使底层优化到位,代码写法不当仍会引发不必要竞争:
-
别在 computeIfAbsent / putIfAbsent 里执行耗时操作:比如
map.computeIfAbsent(key, k -> callRemoteService()),一旦锁住桶,远程调用会拖慢所有对该桶的写请求 -
慎用 putIfAbsent 替代业务锁:它只保证“键不存在时插入”,不包后续逻辑原子性;且
putIfAbsent(key, heavyCompute())会先执行计算再判断,浪费资源 -
size() 不要放在高频循环里:它需遍历所有计数单元,开销大且结果可能过期;改用
mappingCount()获取 long 类型近似值
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










