news 2026/8/16 9:42:36

ConcurrentHashMap数据结构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ConcurrentHashMap数据结构

一、ConcurrentHashMap 概述

  • 作用:在多线程环境下,提供高效、线程安全的键值对存储。
  • 特点
    • 支持高并发读写
    • 不允许 null 键和 null 值
    • 元素无序

二、ConcurrentHashMap 的底层结构(JDK8)

1. 分段锁思想

  • JDK7 及之前:采用分段锁(Segment 数组,每个 Segment 维护一部分数据,锁粒度较粗)。
  • JDK8 及以后:取消 Segment,直接用 Node 数组(类似 HashMap),采用CAS + synchronized细粒度锁,每个桶(Node)独立加锁。

2. 数据结构

  • 主体结构是Node<K,V>[] table,每个 Node 存储 key、value、hash、next。
  • 桶内冲突用链表或红黑树(与 HashMap 类似)。
  • 扩容时采用转移数组(forwardingNode)标记,保证并发安全。
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; volatile V val; volatile Node<K,V> next; }

三、核心原理

1. put 操作

  • 先通过 CAS 尝试插入,如果失败再用 synchronized 锁定当前桶。
  • 只锁定单个桶(链表/树头节点),不会影响其他桶的并发操作。
  • 插入或更新时,保证可见性与原子性。

2. get 操作

  • 只读操作,无需加锁,直接遍历链表/树即可(Node 的 value/next 都是 volatile)。

3. 扩容机制

  • 多线程可以协同完成扩容(transfer),每个线程负责部分桶的迁移。
  • 扩容期间,桶会被标记为 forwardingNode,防止并发冲突。

四、常用方法

  • put(K key, V value):插入/更新键值对
  • get(Object key):查询值
  • remove(Object key):删除键
  • size():计算元素个数(JDK8 是近似值,需遍历所有桶)
  • containsKey(Object key):判断是否包含键
  • forEach()compute()merge()等并发安全的批量操作

五、性能分析

  • 高并发读写:分桶锁定,读操作无锁,写操作只锁定单一桶,极大提升并发性能。
  • 扩容高效:多线程协同迁移,减少阻塞。
  • 空间利用率高:与 HashMap 类似,支持链表/红黑树结构。

六、线程安全性与细节

  • 采用 CAS(Compare-And-Swap)和 synchronized 双重保证线程安全。
  • value/next 等字段用 volatile 修饰,保证可见性。
  • 所有操作都不会抛出 ConcurrentModificationException。

七、与 HashMap/Hashtable 对比

Map 实现类线程安全并发性能锁粒度是否允许 null
HashMap无锁允许
Hashtable整体锁不允许
ConcurrentHashMap分桶/细粒度锁不允许

八、典型应用场景

  • 多线程环境下的缓存
  • 并发统计、分布式数据收集
  • Web 服务的会话管理
  • 线程安全的配置映射

九、示例代码

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("apple", 1); map.put("banana", 2); System.out.println(map.get("banana")); // 输出 2 map.forEach((k, v) -> System.out.println(k + ":" + v));

十、面试常问问题

  1. ConcurrentHashMap 的底层结构和锁机制?
  2. 为什么不允许 null 键和 null 值?
  3. JDK8 之前和之后的实现有何区别?
  4. 如何保证高并发安全?
  5. 与 HashMap、Hashtable 的区别?

十一、JDK7 与 JDK8 的实现差异

1. JDK7(Segment 分段锁)

  • 结构:ConcurrentHashMap 由一个 Segment 数组组成,每个 Segment 本质上类似一个小型的 HashMap。
  • 锁机制:每个 Segment 有自己的 ReentrantLock,线程操作时只锁定一个 Segment,提高并发度,但 Segment 数量有限(默认16),并发度受限。
  • 扩容:每个 Segment 独立扩容,互不影响。

2. JDK8(桶级锁 + CAS)

  • 结构:取消 Segment,直接用 Node 数组,和 HashMap 类似。
  • 锁机制:插入时先用 CAS 操作,无需锁;CAS失败时只锁定单个桶(链表/树头节点),进一步细化锁粒度。
  • 扩容:多线程协同扩容,每个线程负责部分桶迁移,提升效率。

十二、CAS(Compare-And-Swap)机制

  • CAS是一种无锁并发技术,底层依赖 CPU 指令,保证原子性。
  • 在 ConcurrentHashMap 中,插入新节点时首先尝试 CAS,如果成功则无需加锁,失败再回退到 synchronized。
  • CAS 主要用于 table 初始化、节点插入等场景。

CAS 示例(JDK8 put 源码片段)

if (tabAt(tab, i) == null) { if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; // 插入成功,无需加锁 }

十三、扩容细节与协作机制

  • 扩容触发:元素数量超过阈值(容量 × 负载因子)时,触发扩容。
  • 协作迁移:多个线程可以同时迁移不同的桶,迁移过程中,桶会被特殊节点(ForwardingNode)标记,防止并发冲突。
  • 迁移过程:每个线程分配一个迁移区间,迁移完成后,旧桶被标记,后续访问自动跳转到新表。

扩容协作示意图

线程A迁移桶0-3,线程B迁移桶4-7 每个桶迁移完毕后标记为 ForwardingNode 访问老表时遇到 ForwardingNode会跳转新表

十四、批量操作与并行计算

ConcurrentHashMap 提供了许多并发安全的批量操作方法(JDK8):

  • forEach(long parallelismThreshold, BiConsumer<? super K,? super V> action)
  • search(long parallelismThreshold, BiFunction<? super K,? super V,? extends U> searchFunction)
  • reduce(long parallelismThreshold, BiFunction<? super K,? super V,? extends U> transformer, BiFunction<? super U,? super U,? extends U> reducer)

这些方法可以根据 CPU 核数自动并行分片处理,提高大数据量下的处理效率。


十五、常见面试陷阱与注意事项

  1. ConcurrentHashMap 为什么不允许 null 键和 null 值?
    • 因为 null 作为返回值无法区分“键不存在”还是“值就是 null”,容易产生歧义。
  2. size() 方法的准确性?
    • JDK8 的 size() 方法是近似值,因为可能有并发修改。需要遍历所有桶,性能较低,不建议在高并发场景频繁调用。
  3. ConcurrentModificationException 会抛吗?
    • 不会。ConcurrentHashMap 的迭代器是弱一致性(weakly consistent),可以在遍历过程中安全并发修改。
  4. 如何选择并发 Map?
    • 低并发场景用 HashMap,高并发用 ConcurrentHashMap,极高并发且需分片锁可自定义分段 Map。

十六、与其他集合的并发安全对比

集合类型并发安全锁粒度迭代器一致性null 支持
HashMap无锁不一致支持
Hashtable整体锁不一致不支持
ConcurrentHashMap分桶/细粒度弱一致性不支持
Collections.synchronizedMap整体锁不一致支持

十七、使用建议

  1. 多线程环境首选 ConcurrentHashMap,避免 HashMap 线程安全问题。
  2. 避免用 size() 统计大数据量,如需精确计数可用 LongAdder 等方案。
  3. 批量操作用并行方法,如 forEach、reduce,提升性能。
  4. 不要用 null 作为 key 或 value,避免异常。
  5. 合理初始化容量,减少扩容带来的性能开销。

十八、典型应用场景

  • 高并发缓存(如本地缓存、分布式缓存)
  • 并发统计(如计数器、流量统计)
  • 线程安全的会话管理、用户状态管理
  • 配置中心、注册表等高并发读写场景

十九、总结

ConcurrentHashMap 是高性能、线程安全的 Map 实现,广泛用于多线程环境。理解其分段锁、CAS、扩容机制等底层原理,有助于写出高并发高可靠的代码。

ConcurrentHashMap 通过 CAS、细粒度锁、协作扩容等机制,实现了高并发下的高性能和线程安全。理解其底层原理有助于在实际开发和面试中写出高质量的并发代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/9 17:28:46

终极视频下载工具:闪电级异步下载解决方案

在当今数字时代&#xff0c;视频下载工具已成为我们获取网络资源的重要助手。今天介绍的 bilix 项目&#xff0c;正是一款专为追求效率的用户设计的高效视频下载利器&#xff0c;它不仅支持bilibili平台&#xff0c;还扩展至多个视频网站&#xff0c;为用户提供真正的一站式直播…

作者头像 李华
网站建设 2026/7/26 21:31:43

LMMS音乐制作神器:从零基础到专业创作的完整攻略

LMMS作为一款功能强大的跨平台开源数字音频工作站&#xff0c;为音乐爱好者提供了完整的音乐创作解决方案。无论你是刚入门的音乐制作新手还是经验丰富的专业创作者&#xff0c;这款免费软件都能满足从编曲作曲到混音母带的全部需求。 【免费下载链接】lmms Cross-platform mus…

作者头像 李华
网站建设 2026/7/28 20:59:03

CursorPro免费助手:一键解锁AI编程无限额度的终极方案

CursorPro免费助手&#xff1a;一键解锁AI编程无限额度的终极方案 【免费下载链接】cursor-free-everyday 完全免费, 自动获取新账号,一键重置新额度, 解决机器码问题, 自动满额度 项目地址: https://gitcode.com/gh_mirrors/cu/cursor-free-everyday 在AI编程助手成为开…

作者头像 李华
网站建设 2026/8/15 20:19:20

为什么顶级团队开始用Open-AutoGLM做UI自动化?,揭秘背后的3大技术突破

第一章&#xff1a;Open-AutoGLM可以做ui自动化吗Open-AutoGLM 是一个基于大语言模型的开源自动化框架&#xff0c;旨在通过自然语言指令驱动各类自动化任务。虽然其核心设计聚焦于代码生成与任务编排&#xff0c;但通过扩展集成&#xff0c;它具备执行 UI 自动化的潜力。能力边…

作者头像 李华
网站建设 2026/7/26 17:28:25

YOLO模型镜像免费试用,助力企业快速构建视觉系统

YOLO模型镜像免费试用&#xff0c;助力企业快速构建视觉系统 在智能制造车间的质检线上&#xff0c;一台工控机正实时分析高速运转的传送带画面——焊点是否完整、元件有无错位&#xff0c;毫秒级响应的背后&#xff0c;是一套无需从零搭建的目标检测系统。这并非某个大厂专属的…

作者头像 李华
网站建设 2026/7/30 5:57:35

智谦开源Open-AutoGLM实战指南:5步实现零代码AI模型自动构建

第一章&#xff1a;智谦开源Open-AutoGLM实战指南&#xff1a;5步实现零代码AI模型自动构建Open-AutoGLM 是由智谦团队推出的开源自动化大语言模型构建平台&#xff0c;支持无需编码即可完成从数据准备到模型部署的全流程。用户可通过图形化界面或配置文件驱动系统自动完成特征…

作者头像 李华