CurrentHashMap

335 阅读4分钟

ConcurrentHashMap的实现原理,非常详细,一文吃透!-阿里云开发者社区

ConcurrentHashMap 如何保证线程安全?

  • JDK7:分段锁机制,不同 Segment 可并行写入

  • JDK8:桶级别锁(头节点加 synchronized)+ CAS(如 putVal()

主要就是为了应对hashmap在并发环境下不安全而诞生的,ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响。

我们都知道Map一般都是数组+链表结构(JDK1.8该为数组+红黑树)。

ConcurrentHashMap避免了对全局加锁改成了局部加锁操作,这样就极大地提高了并发环境下的操作速度,由于ConcurrentHashMap在JDK1.7和1.8中的实现非常不同,接下来我们谈谈JDK在1.7和1.8中的区别。

ConcurrentHashMap 是 Java 中用于实现线程安全的哈希表(Map)实现类,位于 java.util.concurrent 包中。它在保证高并发性能的同时,提供了线程安全的操作机制,是多线程环境下推荐使用的 Map 实现。


一、ConcurrentHashMap 的核心特性

特性描述
线程安全多个线程可以同时读写而不会导致数据不一致或抛出异常。
高性能相比 Collections.synchronizedMap(new HashMap())Hashtable,性能更高,因为它采用了分段锁机制。
弱一致性迭代器迭代器不会抛出 ConcurrentModificationException,但可能不会立即反映最新的修改。

二、实现原理(以 JDK 1.8 为例)

1. 结构设计:数组 + 链表 + 红黑树

  • ConcurrentHashMap 使用类似于 HashMap 的结构:数组 + 链表/红黑树
  • 每个桶(bucket)对应一个链表或红黑树,当链表长度超过阈值(默认为 8)时转换为红黑树,提升查找效率。

2. CAS + synchronized 控制并发

  • 不再使用 Segment 分段锁(JDK 1.7 及之前),而是采用更细粒度的锁机制:
    • CAS(Compare and Swap) :用于无锁更新操作,如 putIfAbsentreplace 等。
    • synchronized:对某个桶(链表或红黑树的根节点)加锁,只锁定当前正在操作的节点,而不是整个 Map。

3. 扩容机制优化

  • 在扩容时,支持多线程并发迁移(transfer),每个线程负责一部分桶的迁移工作。
  • 扩容过程中允许继续读写,提升了并发性能。

三、线程安全的方法

方法是否线程安全说明
get(Object key)无锁,使用 volatile 保证可见性
put(K key, V value)CAS + synchronized 实现线程安全
remove(Object key)同上
putIfAbsent(K key, V value)如果不存在才插入,利用 CAS
computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)如果存在则计算并更新
forEach(BiConsumer<? super K, ? super V> action)支持并发遍历
keySet() / values() / entrySet()⚠️ 弱一致性迭代过程不阻塞,但可能看到旧数据

四、与 Hashtable 和 SynchronizedMap 的区别

对比项ConcurrentHashMapHashtableCollections.synchronizedMap(new HashMap())
锁粒度桶级别(更细)整个 Map(粗)整个 Map(粗)
性能
允许 null 值key/value 都不允许为 nullkey/value 都不允许为 nullkey/value 都不允许为 null
迭代器弱一致性fail-fastfail-fast
扩容策略并发扩容单线程扩容单线程扩容

五、应用场景

场景推荐使用 ConcurrentHashMap 的原因
缓存系统高并发下频繁读写缓存键值对
统计计数器多线程统计访问次数等信息
注册中心存储服务实例和服务名之间的映射关系
状态管理多线程共享状态变量,如任务执行状态

六、注意事项

  1. 不能保证复合操作的原子性
    if-putput-if-absent,需要使用 putIfAbsentcomputeIfPresent 等方法。
  2. 迭代器是弱一致性的
    在迭代期间其他线程修改了内容,迭代器可能不会立即感知到变化。
  3. 不支持 null 键和 null 值
    为了避免歧义(null 表示不存在还是存在但值为 null)。
  4. 不要依赖其顺序性
    ConcurrentHashMap 不保证元素的顺序。

七、总结

ConcurrentHashMap 是 Java 并发编程中最常用的线程安全 Map 实现,具有以下优势:

✅ 高并发性能
✅ 线程安全
✅ 支持并发扩容
✅ 提供丰富的线程安全操作方法

合理使用 ConcurrentHashMap 可以显著提高多线程程序的性能和稳定性,适用于大多数并发场景下的键值存储需求。