多线程下的 ConcurrentHashMap 精要解析 02
2024-02-20 16:37:31
在 ConcurrentHashMap 的源码分析中,put() 方法是一个重点。它负责将键值对插入哈希表,并在多线程环境下保证线程安全。
public V put(K key, V value) {
return putVal(key, value, false, true);
}
put() 方法首先调用 putVal() 方法,它是一个私有方法,负责实际的插入操作。putVal() 方法有四个参数:key、value、onlyIfAbsent 和 evict。onlyIfAbsent 表示只有在键不存在的情况下才插入值,evict 表示如果哈希表已满,是否驱逐最老的条目。
在 putVal() 方法中,首先根据 key 计算哈希值,然后根据哈希值找到对应的桶。如果桶不存在,则创建一个新的桶。如果桶已存在,则将键值对插入桶中。
Segment<K,V> segment = (Segment<K,V>)table[hash & (table.length - 1)];
插入操作可能导致桶中的条目数超过阈值。如果超过阈值,则需要对哈希表进行扩容。扩容操作会创建一个新的哈希表,并将旧哈希表中的条目复制到新的哈希表中。
if (segment.count >= threshold) {
expandTable();
}
在 putVal() 方法的最后,如果插入操作成功,则返回旧值,否则返回 null。
if (segment.put(key, value, hash, onlyIfAbsent, evict))
return null;
else
return segment.get();
put() 方法是 ConcurrentHashMap 源码分析的重点方法,这里涉及到并发扩容,桶位寻址等等...JDK1.8 ConcurrentHashMap 的结构图:
1、put(K key, V valu
/--------------\
/ \
_________/ \_________
/ \ / \
rootSegmentArray| tailList |segmentArray| headList |
[] | [] [] | []
^ ^
| |
segment 0 segment 1
在 ConcurrentHashMap 中,哈希表被划分为多个段(Segment),每个段由一个 Segment 对象管理。Segment 对象包含一个哈希表,以及一个链表,链表中存储着哈希表中溢出的条目。
当向 ConcurrentHashMap 中插入一个键值对时,首先根据键计算哈希值,然后根据哈希值找到对应的段。如果段中没有找到对应的键,则将键值对插入段中的哈希表。如果段中的哈希表已满,则将键值对插入段中的链表。
当段中的链表长度超过阈值时,段会进行扩容。扩容操作会创建一个新的哈希表,并将链表中的条目复制到新的哈希表中。
ConcurrentHashMap 的 put() 方法是线程安全的。当多个线程同时向 ConcurrentHashMap 中插入键值对时,ConcurrentHashMap 会使用锁来保证线程安全。锁的粒度是段,即每个段都有自己的锁。当一个线程试图修改一个段时,它必须先获得该段的锁。
ConcurrentHashMap 的 put() 方法是高性能的。ConcurrentHashMap 使用分段锁来减少锁竞争,并使用链表来处理哈希表中的溢出条目。这两种技术可以有效地提高 ConcurrentHashMap 的性能。