面灵AI→

Java 秋招一面复盘:HashMap 扩容、锁升级与项目深挖踩坑

时间
2026-10
来源
牛客网

《面试题目》

  1. HashMap 的扩容底层逻辑是什么?
  2. synchronized 锁的升级过程是怎样的?
  3. Spring Cloud 里网关、注册中心、熔断各自是做什么的?一次请求在它们之间是怎么流转的?
  4. 你项目里的库存扣减是怎么做的?怎么防止超卖?
  5. 项目里缓存一致性是怎么保证的?

《参考解析》

「讲不出关联」才是八股失分的真正原因

HashMap 扩容本身并不难:默认容量 16、负载因子 0.75,阈值等于容量乘 0.75;JDK 8 是插入后再判断是否超过阈值,扩容时容量翻倍,元素的新位置由 hash & oldCap 决定——为 0 留在原下标,不为 0 则移到「原下标 + oldCap」,红黑树同样按这两条链拆分,长度退到 6 以下还原成链表。但面试官想听的不是这些孤立数字,而是它们之间为什么这样设计:负载因子取 0.75 是空间与冲突概率的折中;容量取 2 的幂是为了让 hash & (n-1) 等价于取模又不用真的做除法;扩容贵在 rehash 与数据搬迁,JDK 7 头插法在并发扩容下还会成环,所以并发场景要用 ConcurrentHashMap。锁升级也是同一条道理:无锁 → 偏向锁(只有一个线程进来,Mark Word 记线程 ID,连 CAS 都省掉)→ 轻量级锁(出现第二个线程,撤销偏向,用 CAS 抢 Mark Word 里的指针,失败就自旋)→ 重量级锁(自旋到阈值仍抢不到,膨胀成 monitor,未抢到的线程进 EntryList 阻塞,涉及用户态到内核态切换)。把每个状态「为什么会被触发、代价是什么」讲出来,再补上锁粗化、锁消除、以及 JDK 15 起偏向锁默认关闭,这条线才完整。

项目深挖:库存扣减与缓存一致性是最常被戳穿的两处

库存扣减要分层答:数据库侧用带条件的原子更新 update stock = stock - 1 where id = ? and stock >= 1(或加 version 做乐观锁)保证不超卖;秒杀这类高并发场景把库存预热到 Redis,用 Lua 脚本原子扣减,数据库只做最终落账,前面再配限流、异步下单、幂等 token 防重复提交,失败时回补库存。别只答「加个锁」——要说明为什么不用悲观锁(长时间占用连接、吞吐差),以及 Redis 扣减成功而落库失败时怎么兜底对账。缓存一致性先承认业务能接受的是最终一致,再按加固程度排方案:先更新数据库再删缓存 + 过期时间兜底,进一步是延迟双删,最稳的是订阅 binlog 异步失效(Canal),并讲清每种方案留下的失败窗口(删除失败、并发回填旧值、主从延迟)。项目只能撑五分钟的根因通常不是技术太浅,而是简历上写的是「做了什么」而不是「怎么决策、权衡了什么」,每个写上去的技术点都要准备三层:为什么用、怎么实现的、出了什么问题怎么处理。

复盘方法:把知识串成线、把链路画成图、把一个项目真跑通

作者给的复习思路值得照搬。第一,把 JavaSE、并发、JVM、数据库当一条线重新串一遍,用「人话」解释每个知识点——能对着白板讲给不懂技术的人听懂,才算真理解,而不是记住了名词。第二,对 Spring Cloud 这类分布式组件,自己动手画一张请求流转图:请求进网关 → 从注册中心拿到实例列表 → 负载均衡选中一个 → 调用服务 → 触发熔断或降级,画得出来就说明知道组件之间怎么配合,而不是各背各的。第三,项目层面别再拿只写过 CRUD 的 demo 撑简历,从网关到数据库完整跑通一条链路,把每个环节的参数、失败分支都亲手摸过,才扛得住库存扣减、缓存一致性这种深挖。面试现场被问懵时不要沉默硬编,把你知道的部分和推理方向说出来——面试官通常会顺着你的思路给提示,答对一半远比编一个错的强。