ConcurrentHashMap 是 Java 中用于高并发场景的线程安全的哈希表实现。它在 JDK 7 和 JDK 8 中的实现原理有显著的区别,主要是为了提高并发性能和扩展性。下面我们从原理、实现细节和版本区别等方面详细分析。
1. ConcurrentHashMap 的设计目标
-
线程安全:支持高并发读写操作。
-
高性能:尽量减少锁的竞争,提高并发性能。
-
扩展性:支持动态扩容,适应数据量的增长。
2. JDK 7 中的 ConcurrentHashMap
实现原理:
-
分段锁(Segment Locking):
-
JDK 7 中的
ConcurrentHashMap
使用分段锁机制,将整个哈希表分成多个段(Segment
),每个段是一个独立的哈希表,拥有自己的锁。 -
每个段相当于一个小的
HashTable
,锁的粒度从整个表缩小到段级别,减少了锁竞争。
-
-
数据结构:
-
每个段内部是一个数组 + 链表的结构。
-
通过
Segment
数组和HashEntry
链表实现。
-
核心代码:
final Segment<K,V>[] segments; // 分段数组
static final class Segment<K,V> extends ReentrantLock {
transient volatile HashEntry<K,V>[] table; // 哈希表
}
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
}
优点:
-
分段锁减少了锁竞争,提高了并发性能。
-
适合中等并发场景。
缺点:
-
分段锁的粒度仍然较大,高并发场景下性能有限。
-
实现复杂,代码维护成本高。
3. JDK 8 中的 ConcurrentHashMap
实现原理:
-
CAS + synchronized:
-
JDK 8 中的
ConcurrentHashMap
抛弃了分段锁,改用CAS
(Compare-And-Swap)和synchronized
实现线程安全。 -
锁的粒度从段级别缩小到桶级别(每个桶是一个链表或红黑树),进一步减少了锁竞争。
-
-
数据结构:
-
使用数组 + 链表 + 红黑树的结构。
-
当链表长度超过阈值(默认 8)时,链表会转换为红黑树,提高查找性能。
-
核心代码:
transient volatile Node<K,V>[] table; // 哈希表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // 红黑树节点
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // 双向链表
boolean red;
}
优点:
-
锁粒度更小,并发性能更高。
-
数据结构更高效,链表和红黑树的结合提高了查找性能。
-
实现更简洁,代码更易维护。
缺点:
-
实现复杂度较高,尤其是红黑树的插入、删除和平衡操作。
4. JDK 7 和 JDK 8 的区别
特性 | JDK 7 | JDK 8 |
---|---|---|
锁机制 | 分段锁(Segment )。 | CAS + synchronized (桶级别锁)。 |
数据结构 | 数组 + 链表。 | 数组 + 链表 + 红黑树。 |
锁粒度 | 段级别(较大)。 | 桶级别(较小)。 |
并发性能 | 中等。 | 高。 |
扩容机制 | 分段扩容。 | 整体扩容。 |
实现复杂度 | 较高。 | 较高(红黑树操作复杂)。 |
5. 核心操作原理
(1) put 操作
-
JDK 7:
-
根据键的哈希值定位到对应的段。
-
获取段的锁,然后在段内的哈希表中插入或更新键值对。
-
-
JDK 8:
-
根据键的哈希值定位到对应的桶。
-
如果桶为空,使用 CAS 插入节点。
-
如果桶不为空,使用
synchronized
锁住桶的头节点,然后插入或更新键值对。 -
如果链表长度超过阈值,将链表转换为红黑树。
-
(2) get 操作
-
JDK 7:
-
根据键的哈希值定位到对应的段。
-
在段内的哈希表中查找键值对。
-
-
JDK 8:
-
根据键的哈希值定位到对应的桶。
-
在链表或红黑树中查找键值对。
-
(3) 扩容
-
JDK 7:
-
分段扩容,每个段独立扩容。
-
-
JDK 8:
-
整体扩容,所有桶一起扩容。
-
6. 使用场景
-
JDK 7:
-
适合中等并发场景。
-
-
JDK 8:
-
适合高并发场景,性能更高。
-
7. 总结
特性 | JDK 7 | JDK 8 |
---|---|---|
锁机制 | 分段锁。 | CAS + synchronized 。 |
数据结构 | 数组 + 链表。 | 数组 + 链表 + 红黑树。 |
锁粒度 | 段级别。 | 桶级别。 |
并发性能 | 中等。 | 高。 |
实现复杂度 | 较高。 | 较高。 |
8. 一句话总结
-
JDK 7:分段锁机制,适合中等并发场景。
-
JDK 8:CAS +
synchronized
,锁粒度更小,适合高并发场景。