ConcurrentHashMap 深度解析:数据结构、线程安全与扩容机制
ConcurrentHashMap 深度解析:从数据结构到线程安全的底层实现 一、道格·李为什么需要重新设计一个并发哈希表 Java 1.0 提供了 Hashtable——一个线程安全的 Map 实现。它的线程安全策略很简单:在所有 public 方法上加 synchronized。这个策略正确但不实用——任何时候只有一个线程能操作整个表,即使两个线程操作的是不同的键。在 1.0 时代并发不常见时还凑合,到了 Java 5 时代,服务器端的多线程访问同一个缓存 Map 已经是常规操作,Hashtable 的全局锁成了吞吐量的天花板。 HashMap 是 Hashtable 的非线程安全替代,性能好得多,但一旦多线程并发 put,就会出现数据丢失、size 计数错误,甚至在 JDK 7 扩容时出现链表成环导致 CPU 100%。 道格·李在设计 ConcurrentHashMap 时面临的问题是:既要保证线程安全(不能丢数据),又要提供接近 HashMap 的并发吞吐量(不能全局锁)。这是两个互相矛盾的目标,传统的 synchronized 方案只能取其一。 道格·李的解决方案是把锁的粒度从"整张表"缩小到"单个桶"。JDK 5 ~ 7 中用了 Segment 分段锁(16 个段,每段独立加锁),JDK 8 进一步细化为桶级别 CAS + synchronized——对空桶用 CAS 无锁插入,对非空桶只锁链表/红黑树的头节点。这种设计让 16 个线程同时操作 16 个不同桶时完全无竞争,并发度从 Hashtable 的 1 提升到桶的数量级。 🗺️ 二、ConcurrentHashMap 的数据结构 JDK 8 的 ConcurrentHashMap 放弃了 JDK 7 的 Segment 分段锁设计,直接采用与 HashMap 相似的结构: Node 数组 + 链表 + 红黑树 。 ...