腾讯面试必杀题:JDK 7 和 8 的 ConcurrentHashMap 对比,回答好这一题直接定级P6
前言作为面试中的常青树ConcurrentHashMap 的演进史几乎就是一部 Java 并发编程的进化史。能清晰说出 JDK 7 和 8 的区别并解释设计背后的权衡是区分普通程序员和架构师的分水岭。一、从一个线上故障说起某互联网金融平台在一次大促活动中突然出现大量线程阻塞接口响应时间从 50ms 飙升到 10 秒。排查发现罪魁祸首是一个使用 ConcurrentHashMap 做本地缓存的配置中心组件。开发同学百思不得其解ConcurrentHashMap 不是线程安全的吗怎么会导致线程阻塞真相是他们用的 JDK 7而 ConcurrentHashMap 在 JDK 7 中的并发度受限于 Segment 数量当某个 Segment 竞争激烈时所有落入该 Segment 的线程都会被阻塞。这个案例引出我们今天的主角JDK 7 和 JDK 8 中 ConcurrentHashMap 的架构巨变。二、JDK 7 时代的 ConcurrentHashMap分段锁的得与失2.1 数据结构全景图JDK 7 中的 ConcurrentHashMap 由 Segment 数组 和 HashEntry 数组 组成textConcurrentHashMap├─ Segment[0] (继承 ReentrantLock)│ ├─ HashEntry[]│ ├─ count, modCount, threshold, loadFactor├─ Segment[1]│ ├─ HashEntry[]└─ …核心设计理念分段锁Lock Striping。Segment 扮演锁的角色每个 Segment 继承自 ReentrantLock独立锁定自己管理的 HashEntry 数组。默认并发度 16即创建 16 个 Segment理论上支持 16 个线程同时写操作前提是写不同的 Segment。HashEntry 就是链表节点和 HashMap 一样通过拉链法解决哈希冲突。2.2 写操作源码剖析put 方法javapublic V put(K key, V value) {SegmentK,V s;if (value null) throw new NullPointerException();int hash hash(key);int j (hash segmentShift) segmentMask; // 定位 Segmentif ((s (SegmentK,V)UNSAFE.getObject(segments, (j SSHIFT) SBASE)) null)s ensureSegment(j);return s.put(key, hash, value, false); // 委托给 Segment 的 put}关键在 Segment.put 中javafinal V put(K key, int hash, V value, boolean onlyIfAbsent) {// 尝试获取锁自旋或阻塞HashEntryK,V node tryLock() ? null : scanAndLockForPut(key, hash, value);try {// 已经拿到锁操作链表// …} finally {unlock();}}优点多个 Segment 之间可以并发写入大大提升了并发度。缺点锁粒度较粗一个 Segment 保护一整段 HashEntry 数组即使多个写操作落在同一个 Segment 的不同 HashEntry 上也会互斥。内存占用大每个 Segment 都是一个重入锁内部维护了锁状态且 Segment 数组大小在构造时确定不能动态调整。并发度固定默认 16如果机器有 32 核也无法利用更多核心。2.3 读操作几乎无锁读操作get不需要加锁因为 HashEntry 的 value 和 next 都被声明为 volatile保证了可见性。这是 JDK 7 的巧妙设计。三、JDK 8 的革命性重构锁粒度细化到极致JDK 8 完全重构了 ConcurrentHashMap抛弃了 Segment直接使用 Node 数组 CAS synchronized把锁的粒度从段级别缩小到桶级别。3.1 数据结构大变身textConcurrentHashMap├─ Node[] table数组├─ TreeBin红黑树根节点替代链表├─ TreeNode红黑树节点└─ ForwardingNode扩容时使用核心变化放弃分段锁不再使用 Segment直接对每个桶table 数组的每个槽位进行并发控制。引入红黑树当链表长度超过 8 时转为红黑树TreeBin优化查询性能。使用 synchronized 替代 ReentrantLock锁的粒度更小且 synchronized 在 JDK 6 之后经过大量优化偏向锁、轻量级锁、重量级锁升级性能已经不输 ReentrantLock。增加计数辅助类使用 CounterCell 数组和 baseCount 来高效统计元素个数避免全局计数器的竞争。3.2 put 操作流程源码级解析javafinal V putVal(K key, V value, boolean onlyIfAbsent) {if (key null || value null) throw new NullPointerException();int hash spread(key.hashCode());int binCount 0;for (NodeK,V[] tab table;) {NodeK,V f; int n, i, fh;if (tab null || (n tab.length) 0)tab initTable(); // 懒初始化else if ((f tabAt(tab, i (n - 1) hash)) null) {// 桶为空CAS 尝试插入无锁if (casTabAt(tab, i, null, new NodeK,V(hash, key, value, null)))break;}else if ((fh f.hash) MOVED)tab helpTransfer(tab, f); // 帮助扩容else {V oldVal null;synchronized (f) { // 锁住桶的头节点if (tabAt(tab, i) f) {if (fh 0) { // 链表节点// 遍历链表插入或覆盖} else if (f instanceof TreeBin) { // 红黑树节点// 红黑树插入}}}// 检查是否需要转为红黑树if (binCount ! 0) {if (binCount TREEIFY_THRESHOLD)treeifyBin(tab, i);return oldVal;}}}addCount(1L, binCount); // 计数增加return null;}关键点桶为空时使用 CAS 无锁插入并发性能极高。桶非空时使用 synchronized 锁住桶的头节点只锁这一个桶其他桶完全不受影响。锁粒度极细。正在扩容当前桶节点是 ForwardingNodehash MOVED当前线程会帮助迁移数据。3.3 为什么放弃 Segment改用 synchronized面试加分项源码级别的理解锁粒度更细JDK 7 中锁的粒度是 Segment包含多个桶JDK 8 锁的粒度是单个桶并发度从 Segment 数量默认 16提升到 table 数组长度默认 16但扩容后可达数万。内存占用减少JDK 8 不再需要维护 Segment 对象和锁状态每个桶只需一个 Node 对象。synchronized 经过优化JDK 6 之后对 synchronized 做了大量优化包括锁升级偏向锁 - 轻量级锁 - 重量级锁在低竞争下性能非常好且 synchronized 是 JVM 内置的可以节省内存。扩容时并发协助JDK 8 引入了多线程并发扩容机制让其他线程在 put 时如果发现正在扩容可以帮忙一起迁移数据大大提高扩容效率。红黑树优化查询JDK 7 中链表过长时查询 O(n)JDK 8 中链表转红黑树最坏查询 O(log n)。四、关键操作的演进对比4.1 计算元素个数 size()JDK 7先尝试不加锁统计 3 次如果前后一致则返回否则锁住所有 Segment 再统计。代价高昂。JDK 8使用 baseCount CounterCell[] 数组。每个线程修改时会先尝试 CAS 更新 baseCount失败则更新到自己的 CounterCell 槽位最后累加。无锁化设计并发性能极高。4.2 扩容机制JDK 7每个 Segment 内部扩容不影响其他 Segment。JDK 8整个 table 数组扩容但支持多线程并发迁移。每个线程负责迁移一部分桶迁移过程中通过 ForwardingNode 标记其他线程遇到该桶时会帮助迁移。这是 JDK 8 并发度大幅提升的关键设计。五、面试官追问JDK 8 的设计还有哪些坑Q1ConcurrentHashMap 的 size() 是绝对精确的吗不是。由于并发修改size() 返回的是一个近似值仅用于监控或非强一致性的场景。如果需要精确计数应使用 mappingCount() 方法它返回 long 型但同样不是实时精确值。Q2ConcurrentHashMap 的 key 和 value 为什么不能为 null在 JDK 8 中依然不能为 null。这是为了与 HashMap 区分同时也是为了避免在多线程环境下出现歧义如果 get 返回 null你无法判断是 key 不存在还是 value 本身就是 null。而 ConcurrentHashMap 是设计用于多线程的为了简化问题直接禁止 null。Q3ConcurrentHashMap 的迭代器是 fail-safe 还是 fail-fast是弱一致性弱一致性迭代器。迭代时不会抛出 ConcurrentModificationException但可能无法反映迭代开始后的最新修改。这适合并发场景。六、总结两张图看懂演进JDK 7 分段锁模型text[Segment0锁] — HashEntry[] — 链表[Segment1锁] — HashEntry[] — 链表[Segment2锁] — HashEntry[] — 链表…并发度 Segment 数锁粒度粗。JDK 8 桶锁模型texttable[] — 桶0CAS或synchronized锁头— 链表/红黑树— 桶1CAS或synchronized锁头— 链表/红黑树— 桶2CAS或synchronized锁头— 链表/红黑树…并发度 table 长度锁粒度极细。核心演进思想锁粒度细化从段锁到桶锁锁机制优化从 ReentrantLock 到 CAS synchronized数据结构优化引入红黑树避免 O(n) 查询并发扩容多线程协助迁移写在最后ConcurrentHashMap 的演进反映了 Java 并发编程从粗粒度悲观锁到细粒度无锁CAS局部悲观锁的演进趋势。理解这些设计背后的权衡不仅能帮你轻松应对面试更能让你在实际开发中做出更好的技术选型。你在项目中遇到过 ConcurrentHashMap 的并发问题吗欢迎在评论区分享你的故事

相关新闻

阿里二面:明明加了索引,查询为什么还是慢?90%的候选人答不到点上

阿里二面:明明加了索引,查询为什么还是慢?90%的候选人答不到点上

前言 索引失效是MySQL性能优化中最基础也最重要的话题,面试官常以此考察你对数据库底层原理的理解深度。 一、问题背景:一个让DBA彻夜难眠的夜晚 "明明字段上有索引,查询却突然变慢10倍!"这是某电商平台DBA小张上周遇到…

2026/7/5 2:16:42 阅读更多 →
jenkins有何特性?jenkins常用功能介绍

jenkins有何特性?jenkins常用功能介绍

jenkins是一个可扩展的持续集成引擎,大家对于jenkins可能并非十分了解。为增进大家对jenkins的认识,本文将从两大方面介绍jenkins:1. Jenkins主要用途与特性,2. Jenkins常用功能介绍。如果你对jenkins具有兴趣,不妨继续…

2026/7/5 2:16:01 阅读更多 →
【NUCLEO-WBA65RI评测】ST无线系列STM32WBA6 DEMO板基本功能测试

【NUCLEO-WBA65RI评测】ST无线系列STM32WBA6 DEMO板基本功能测试

简介ST 全新推出的 STM32WBA6 系列重磅登场,单芯片就能同时支持 Bluetooth LE 和 IEEE 802.15.4 标准,2MB 闪存 512KB RAM 的大存储搭配双区闪存设计,更有 STOP2 模式下仅 5A 的超低功耗(实时时钟开启状态)&#xff0…

2026/5/17 12:53:57 阅读更多 →

最新新闻

YOLO11视频目标检测实战:从环境配置到高级应用

YOLO11视频目标检测实战:从环境配置到高级应用

1. 项目概述 视频目标检测是计算机视觉领域的重要应用场景,而YOLO系列模型因其出色的实时性能成为该任务的首选方案。本文将基于YOLO11模型,详细讲解如何实现视频文件的逐帧检测,并输出带有检测框的可视化视频。 提示:YOLO11是YO…

2026/7/5 2:16:34 阅读更多 →
程序员就业:2026 年还能靠什么拿到,把工具链跑成稳定流程

程序员就业:2026 年还能靠什么拿到,把工具链跑成稳定流程

聊《程序员就业:2026 年还能靠什么拿到,把工具链跑成稳定流程》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要这篇面向准备找工作、跳槽或转型的程序员,但不会把“程序员就业…

2026/7/5 2:16:34 阅读更多 →
NSK滚珠丝杠W3205SS技术解析

NSK滚珠丝杠W3205SS技术解析

为您详细整理 W3205SS-1Z-C5Z10 滚珠丝杠的参数规格、技术特点及产品应用。 (温馨提示:您查询的型号命名规则属于 NSK(日本精工) 的标准产品,而非 NTN。以下内容基于 NSK 精机综合样本为您详细解读。) 该型号属于 NSK 的 SS 系列&…

2026/7/5 2:14:33 阅读更多 →
自定义布局控件

自定义布局控件

讲到自定义布局控件,我们必须得先谈一下在WPF中自定义控件,在WPF自定义控件你可以选择下图的一些基类作为继承对象,你也可以继承自已有的一些控件,这个就看你的需要了。其实开发WPF自定义控件和开发WinForm、ASP.NET自定义控件基本…

2026/7/5 2:12:33 阅读更多 →
Border

Border

Border 是一个装饰的控件,此控件绘制边框及背景,在 Border 中只能有一个子控件(这个子控件又可以包含多个子控件)。Border 的几个重要属性:Background:用用一个 Brush 对象来绘制背景 ;BorderBrush:用一个B…

2026/7/5 2:12:33 阅读更多 →
SRWE窗口分辨率编辑器:终极游戏截图与多屏适配解决方案

SRWE窗口分辨率编辑器:终极游戏截图与多屏适配解决方案

SRWE窗口分辨率编辑器:终极游戏截图与多屏适配解决方案 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE SRWE(Simple Runtime Window Editor)是一款功能强大的开源窗口分辨率自…

2026/7/5 2:10:33 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻