深入理解-ConcurrentHashMap:JDK-1-7-与-1-8-的演进与实现原理
深入理解 ConcurrentHashMap:JDK 1.7 与 1.8 的演进与实现原理
在高并发场景中,ConcurrentHashMap
作为 Java 提供的线程安全的哈希表实现,被广泛应用于缓存、连接池、线程计数等业务中。它的高性能来源于底层的数据结构与锁机制的优化。本文将带你一步步深入了解 JDK 1.7 和 1.8 中 ConcurrentHashMap
的实现原理、区别以及背后的技术设计。
一、JDK 1.7:分段锁机制(Segment Lock)
在 JDK 1.7 中,ConcurrentHashMap
的核心设计理念是分段锁机制(Segment Locking)。其结构如下:
✳️ 数据结构解析:
- 整体结构:由一个数组
Segment[]
组成,每个 Segment 是一个哈希表HashEntry[]
。 - 锁粒度:每个 Segment 内部使用
ReentrantLock
锁,意味着多个线程只要访问的是不同 Segment,可以并行执行。 - 默认分段数:16(可配置),最多支持 16 个线程同时写操作。
✅ 优点:
- 通过锁分段降低锁竞争,提升并发性能;
- 避免了全表加锁的问题。
❗️缺点:
- Segment 数量固定,粒度控制有限;
- Segment 本质上仍是哈希表,链表冲突仍可能造成性能下降;
- 相较于 JDK 1.8,代码结构复杂。
二、JDK 1.8:Synchronized → CAS + Synchronized + 分段链表
JDK 1.8 对 ConcurrentHashMap
的架构进行了彻底重构,采用了一种更细粒度、更现代的设计方式:
✳️ 新的数据结构:
- 核心结构是一个
Node[]
数组,原本的Segment[]
被彻底移除; - 链表或红黑树:每个数组元素是一个链表(低冲突)或红黑树(高冲突时自动转换);
- 每个链表或树节点之间通过
next
连接; - 支持动态扩容。
🔐 锁机制变化:
- 使用 CAS(Compare-And-Swap) 来进行无锁更新;
- 对于链表或树的写操作,采用
synchronized
锁控制(加在首节点); - 高并发下,锁粒度进一步缩小至单个 bin(桶);
✅ 优点:
- 更高并发性能;
- 精细化锁控制,避免了 Segment 带来的扩展限制;
- 使用
CAS + synchronized
实现读写分离,提升线程安全性。
三、源码解析:put 操作流程分析(JDK 1.8)
让我们深入源码,看一下 put 操作是如何保障线程安全的:
final V putVal(K key, V value, boolean onlyIfAbsent) {...if ((tab = table) == null || (n = tab.length) == 0)n = (tab = initTable()).length;...if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {...synchronized (p) {...if (e != null) {...e.val = value;}}}...
}
📌 关键细节:
tab[i] == null
说明该桶为空,可直接 CAS 插入;- 如果不为空,会尝试加锁
synchronized (p)
来进行链表插入或值覆盖; - 红黑树的处理逻辑则更复杂一些,但也是在同步块中完成。
四、size 统计问题
在 JDK 1.7 中,size 统计是个麻烦的问题——需要遍历所有 Segment,逐一加锁,效率低下。
JDK 1.8 使用了类似于 LongAdder
的设计(源码中叫 CounterCell[]
),每个线程有一个计数单元,最后统一累加即可,大大提升了统计效率和并发性能。
final void addCount(long x, int check) {CounterCell[] as; long b, v; int m;...if ((as = counterCells) != null || !casBaseCount(b = baseCount, b + x)) {...as[i].value += x;}
}
这是 LongAdder
的经典用法,避免了多线程竞争导致的性能下降。
五、CAS 简析
在 JDK 1.8 中大量使用的 CAS,全称是 Compare-And-Swap,核心思想是:
- 比较某个变量当前值是否为预期值;
- 若是,则更新为新值;
- 否则,不做处理,重试。
它是一种无锁的原子操作,是构建高性能并发容器的基础。这里就不详细介绍了,详细说明欢迎点进我的个人主页中查找
六、总结对比
特性 | JDK 1.7 | JDK 1.8 |
---|---|---|
数据结构 | Segment + HashEntry[] | Node[] + 链表/红黑树 |
锁机制 | Segment级别锁(ReentrantLock) | synchronized + CAS |
并发性能 | 中等 | 高 |
size统计 | 全锁 + 遍历Segment | LongAdder 分段计数 |
扩展性 | 受限于 Segment 个数 | 动态扩容,粒度更细 |