concurrenthashmap
ConcurrentHashMap 并发设计(JDK 8)
ToC
- 基本结构与无锁读
- 插入与扩容(CAS + 链/树)
- 计数与 size 计算
- 与 Hashtable/同步 Map 对比
- 复合操作与 compute 系列
基本结构与无锁读
与 HashMap 类似使用桶数组,但在并发下:
- 读大多为无锁:定位桶后若为不可变结构可直接读取;
- 写使用
synchronized
作用于桶头节点或使用 CAS 协作; - 扩容采用转移线程协作(迁移任务分段)。
插入与扩容(CAS + 链/树)
典型流程:
- 桶为空:使用 CAS 放置首节点;
- 桶为
ForwardingNode
:表示正在扩容,帮助迁移后重试; - 否则对桶头加锁后在链/树中查找插入;
树化/退化阈值与 HashMap 相似,但实现细节不同(TreeBin
包装)。
计数与 size 计算
- 计数使用
CounterCell
分桶累加,减少热点竞争; size()
在并发下可能返回近似值,可使用mappingCount()
获取long
近似计数;- 强一致计数需遍历,代价高。
与 Hashtable/同步 Map 对比
Hashtable
对所有操作加全表锁,吞吐差;Collections.synchronizedMap
包装器也是粗粒度锁;ConcurrentHashMap
提供更细粒度同步与无锁读,支持更丰富复合操作。
复合操作与 compute 系列
computeIfAbsent/compute/merge
等在并发下是“原子”的,但计算函数内部不要做阻塞/长耗时操作以免拖慢桶锁持有时间。
示例:
ConcurrentHashMap<String, Expensive> cache = new ConcurrentHashMap<>();
Expensive v = cache.computeIfAbsent(key, k -> load(k));