面灵AI→

携程 AI 面真题:HashMap、AQS 与百万 QPS 架构

轮次
AI面
时间
2026-09
来源
牛客网

《面试题目》

  1. 讲一次你发现问题、主动推动落地解决方案的经历。
  2. HashMap 底层相关的八股。
  3. AQS 原理与锁相关问题。
  4. ISO 七层网络模型,分层及对应作用。
  5. 十万、百万 QPS 下的业务架构怎么设计?
  6. 描述一个项目中需要快速上手陌生技术栈的经历,讲你的学习路径。
  7. 平时通过哪些渠道学习 AI 相关技术?最近在研究什么内容?
  8. 深挖实习经历:为什么选择携程,为什么投递这个 BU 岗位?
  9. 简历项目连环追问:抠实现细节、踩坑点、线上问题。
  10. 同月 Java 开发岗场次:用 Redis 设计千万用户的排行榜?
  11. 同月 Java 开发岗场次:CompletableFuture 和普通 Future 的区别?

《参考解析》

**AI 面的追问机制:下一问是从你嘴里长出来的:**这类面试没有真实面试官,追问由上一轮回答的转写文本驱动,模型会显式抓取技术名词再展开。所以有两个可用的结论:不想被深挖的方向别主动提,想被问到的方向就先把那个词说出口——回答里出现「提示词约束」「线程池」这类具体词,下一问大概率就落在那儿。另外计时一旦开始无法撤回,进考场前先确认自己投的岗位和场次,别拿一份没复习完的卷子去试。

**HashMap 底层:数组 + 链表 + 红黑树:**常考点集中在几处:哈希扰动函数把高位异或到低位,是为了让散列结果在容量较小时也分布均匀;负载因子 0.75 是空间与冲突概率的折中,超过阈值扩容成两倍并重新分布(1.8 起按高位判断原地不动或迁移,避免重新计算 hash);链表长度到 8 且容量不小于 64 才树化,退化阈值是 6,留出缓冲避免频繁转换;1.8 把头插改成尾插是为了修掉并发扩容成环的问题,但 HashMap 依然不是线程安全的,并发写用 ConcurrentHashMap。要能顺带说出「为什么是 8」——按泊松分布,链表长度到 8 的概率已经极低。

**AQS:一个 volatile state 加一条等待队列:**AQS 把「能不能拿到锁」交给子类实现(tryAcquire/tryRelease),把排队、阻塞、唤醒这套模板方法留在基类,所以 ReentrantLock、Semaphore、CountDownLatch 都能复用同一套骨架。要讲清楚:state 用 CAS 修改,等待队列是 CLH 变体(双向、带状态标记),节点入队后靠 LockSupport.park 阻塞、前驱释放时唤醒;独占与共享两种模式决定唤醒是找一个还是传播一批;可重入靠 state 累加并记录持有线程,释放到 0 才真正放锁;公平锁多一步检查队列里有没有前驱,非公平锁上来先抢一次,因此有吞吐更高但可能饥饿的取舍。

**ISO 七层与 TCP/IP 分层:**七层从下到上是物理、数据链路、网络、传输、会话、表示、应用,面试里真正会被追问的是数据链路层——它负责成帧、用 MAC 做物理寻址、用 CRC 做差错检测、做介质访问控制,并决定了交换机与路由器的分工(二层按 MAC 表转发、三层按路由表转发)。TCP/IP 把上三层合成应用层,实际讲四层或五层模型都行,但要能说出每一层的协议代表与数据单元名称(帧、包、段、报文),并能把 ARP、ICMP、TCP、HTTP 这些协议各归其位。

**十万到百万 QPS 的架构:先算账,再堆组件:**这类题最忌讳直接报「加缓存、读写分离、分片」三件套。可复述的框架是四步:① 估容量——峰值 QPS 与均值的关系(通常按 2~3 倍算)、单机承载多少、需要多少台、连接数与带宽是否够;② 分层——DNS/GSLB → CDN 扛静态 → 接入层(LVS/Nginx)→ 网关(鉴权、限流、路由)→ 无状态服务 → 多级缓存(本地 + Redis)→ 分库分表 → MQ 削峰 → 异步落库;③ 按数据特性拆——读多写少就把缓存做厚并处理一致性,写多就先分片再用消息解耦;④ 收尾讲稳定性——热点 key、缓存击穿与雪崩、限流熔断降级、灰度与压测。容量估算给出数字之后,方案自然就收敛了,这也是「分片之后一致性怎么保证」这类追问的入口:分片键选得对不对、跨片事务要不要用最终一致、全局唯一 ID 怎么生成。

行为题与动机题:把「我做了什么」讲成结果:「发现问题并推动落地」用 STAR 讲,但重心放在你个人的判断和推动动作上,结尾一定要有可量化的结果(指标改善了百分之多少、流程从几天缩到几小时),没有数字的行为题听起来就是「参与了」。快速上手陌生技术栈那条,讲清路径而不是决心:先看官方文档跑通最小示例 → 读关键源码或设计文档 → 做一两个对照实验验证理解 → 小范围试用再推广。「为什么选携程、为什么这个 BU」别背「平台大、口碑好」,把它和你投的那个业务单元的实际场景绑在一起讲——你了解它做什么、你哪段经历能接上、你想在里面做成什么。

**CompletableFuture 与 Future:**普通 Future 只有 get 与 isDone,取结果必须阻塞或轮询,多个异步任务之间也没法组合。CompletableFuture 补的是编排能力:thenApply/thenCompose 做串行依赖,thenCombine/allOf/anyOf 做并行聚合,exceptionally/handle/whenComplete 统一处理异常与收尾,并且几乎每个方法都有带 Executor 的重载——这一点很重要,默认用 ForkJoinPool.commonPool,CPU 密集任务混进去会互相影响,线上一般传自己的线程池。还要知道它的回调是在完成线程上执行的,注意上下文传递与超时(orTimeout/completeOnTimeout)。

**Redis 排行榜:ZSet 是标准答案,但要答出边界:**ZSet 底层是跳表 + 哈希表,跳表保证范围查询与排名是 O(log N),哈希表保证按成员取分是 O(1)。基本操作是 ZINCRBY 加分、ZREVRANGE WITHSCORES 取榜、ZREVRANK 查名次,取前 N 名很便宜,取任意深分页才会变慢。真正会被追问的是细节:同分怎么排(把时间戳拼进 score 的小数位或用复合 score)、单 key 大了怎么办(按榜单维度或 uid 取模分片,再合并各片的前 N)、热 key 读压力(本地缓存 + 结果缓存,秒级延迟可接受)、以及榜单单点故障后怎么从明细重建(保留一份可重算的流水)。