JDK1.8 ConcurrentHashMap 通过“分而治之+无锁优先+关键处加锁”实现线程安全:写操作仅锁单个桶头节点,空桶用CAS无锁插入,非空桶才加synchronized;读操作全程无锁,依赖volatile和final保证可见性与不变性;size统计与扩容均采用CAS+分段协作机制,大幅降低竞争。

ConcurrentHashMap 的线程安全不是靠一把大锁撑起来的,而是靠“分而治之 + 无锁优先 + 关键处加锁”三层策略协同实现的。回答时抓住 JDK 1.8 主流实现(当前生产环境主流),讲清逻辑主线,避免堆砌术语。
核心是锁粒度极细:只锁单个桶(bin),不锁整张表
和 Hashtable 对整个 Map 加 synchronized 不同,ConcurrentHashMap 在写操作(put、remove)时,只对目标 hash 桶的第一个节点(链表头或红黑树根)加 synchronized 锁。其他桶完全不受影响,多个线程操作不同桶可完全并发执行。
- 如果桶为空,优先用 CAS 尝试插入新节点——无锁、轻量、快
- 如果桶非空,才对头节点加锁,再执行链表遍历、插入或树化等操作
- 扩容时会分段迁移,新老数组并存,迁移过程也只锁正在处理的桶,而非整个 table
读操作全程无锁,靠 volatile 和 final 保障可见性与不变性
get() 方法不加任何锁,但依然能读到最新值,关键在两个设计:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- Node 的 val 字段用 volatile 修饰,保证写入后对其他线程立即可见
- key、hash、next 字段都声明为 final,节点一旦创建就不会被修改,避免读到半初始化状态
- table 数组本身也是 volatile 声明,确保扩容后新数组引用的更新能被及时感知
计数与扩容靠 CAS + 协作机制,避免全局竞争
size() 统计不依赖锁,而是维护一个 baseCount 变量 + 若干 CounterCell 数组:
立即学习“Java免费学习笔记(深入)”;
- 多数情况下用 CAS 更新 baseCount
- 高并发冲突时,分散到不同 CounterCell 中累加,最后汇总,减少 CAS 失败重试
- 扩容由多个线程协作完成:一个线程触发,其他线程检测到扩容中,会主动帮忙迁移部分桶,提升效率
和 HashMap / Hashtable 的对比要落在“为什么更优”上
不能只说“它线程安全”,要点出设计取舍背后的工程权衡:
- 比 HashMap 安全:规避了多线程 put 导致的链表成环(JDK 1.7)、数据覆盖、size 不准等问题
- 比 Hashtable 高效:Hashtable 是全表锁,同一时刻只能一个线程写;ConcurrentHashMap 支持数十甚至上百线程同时写不同桶
- 比 Collections.synchronizedMap 更灵活:后者是简单包装,所有方法串行化;前者有读写分离、CAS 优化、分段扩容等深度优化

















