本轮 AI+Java中级开发面试题库(带答案)
生成时间:2026-05-20 22:31 题目数量:10道(AI 5道 + Java 5道)
【面试题】1. 请解释Transformer架构中自注意力机制(Self-Attention)的工作原理,以及它在处理长序列时的计算复杂度问题是什么?
【标准答案】自注意力机制的核心是让序列中的每个位置都能"看见"并加权聚合其他所有位置的信息。具体计算分三步:
第一步,输入向量通过三个线性变换得到Query、Key、Value三个矩阵。第二步,Query和Key做点积并除以根号dk进行缩放,然后通过Softmax得到注意力权重矩阵。第三步,用权重矩阵对Value加权求和,得到最终输出。
计算复杂度问题:标准自注意力的时间和空间复杂度都是O(n²),其中n是序列长度。这是因为每个位置都要和其他所有位置计算注意力分数。当序列长度超过几千时,显存占用和计算时间会急剧增长。
实际工程中的优化方案包括:稀疏注意力(只计算部分位置)、线性注意力(用核函数近似)、分块注意力(如Longformer的滑动窗口)、以及Flash Attention这种硬件感知优化。在面试中如果提到你了解这些优化方案,会很加分。
【面试题】2. ConcurrentHashMap在Java 8中的实现相比Java 7有哪些重大改进?为什么这样改进?
【标准答案】Java 7用的是分段锁(Segment),把数据分成多个段,每个段一把锁。并发度最高等于Segment数量(默认16)。问题是:Segment数组初始化后就固定了,无法扩容;单个Segment内的哈希冲突严重时性能下降明显。
Java 8做了三个关键改进:
第一,取消Segment,改用CAS+synchronized锁单个桶。每个Node节点级别加锁,锁粒度更细,理论上并发度可以到数组长度。
第二,引入红黑树优化冲突链表。当链表长度超过8且数组长度≥64时,链表转红黑树,查找从O(n)变成O(logn)。这对哈希冲突严重的场景提升巨大。
第三,扩容时支持多线程协同。多个线程可以同时帮忙迁移数据,每个线程负责一段区间,扩容效率大幅提升。
为什么这样改进?因为Java 7的Segment是重量级锁,每次操作都要CAS整个Segment;而Java 8的synchronized经过JVM大量优化(偏向锁、轻量级锁、重量级锁的锁升级),在低竞争场景下几乎无开销。另外,红黑树解决了长链表的性能陷阱。
【面试题】3. 什么是RAG(检索增强生成)?它解决了大模型的哪些核心问题?生产环境中有哪些关键优化点?
【标准答案】RAG的核心思路是:在生成回答前,先从知识库检索相关文档,把检索结果作为上下文喂给大模型,让模型基于真实资料回答问题。
它解决了大模型的三个核心问题:
第一,知识时效性。大模型训练有截止日期,RAG可以实时检索最新资料。第二,幻觉问题。模型回答被约束在检索到的文档范围内,减少编造。第三,领域知识不足。企业私有数据不在训练语料里,RAG让模型能访问这些知识。
生产环境的关键优化点:
- 检索质量:用混合检索(关键词+向量),用Rerank模型对召回结果精排,Top-K和阈值的调优
- 分块策略:按语义边界切分比固定长度更好,保留上下文窗口,避免关键信息被切断
- 索引更新:增量更新索引而非全量重建,监控数据新鲜度
- 延迟优化:向量数据库选型(Milvus/Qdrant/Pinecone)、预热缓存、检索和生成并行化
- 评估体系:检索的召回率、答案的准确率、端到端的用户满意度,形成数据飞轮持续优化
【面试题】4. Spring事务传播机制中,REQUIRED和REQUIRES_NEW有什么区别?在什么场景下会用到REQUIRES_NEW?
【标准答案】REQUIRED是默认传播行为:如果当前存在事务就加入,没有就新建。REQUIRES_NEW则是:无论如何都新建事务,如果当前有事务就把它挂起。
关键区别在于事务边界:
- REQUIRED下,内层方法和外层方法共享同一个事务。内层抛异常回滚,外层也会回滚;外层捕获异常后想继续执行,但事务已经标记为rollback-only,会抛UnexpectedRollbackException。
- REQUIRES_NEW下,内层方法有独立事务。内层回滚不影响外层,外层回滚也不影响内层已提交的结果。
典型使用REQUIRES_NEW的场景:
日志记录:业务失败回滚了,但失败日志必须保存下来,不能用同一个事务。异步通知:发送通知成功后,即使后续业务失败,通知也不应该回滚。批量处理中的独立单元:比如批量导入100条数据,第50条失败不影响前49条,每条记录用独立事务。
注意事项:REQUIRES_NEW会挂起外层事务,数据库连接池压力会增大。高并发下要谨慎使用,避免连接耗尽。
【面试题】5. 提示词工程中的"Few-shot"和"CoT(思维链)"分别是什么?在实际项目中如何组合使用?
【标准答案】Few-shot就是在提示词中给出几个示例,让模型模仿示例的格式和逻辑来回答。比如让模型做情感分析,先给出"今天心情很好→积极"、"考试考砸了→消极"两个示例,再给出新的句子让模型判断。
CoT(Chain of Thought)是让模型"一步一步思考",把推理过程显式写出来,而不是直接给答案。比如数学题,让模型先列出已知条件、再列方程、再求解、再验证,而不是直接输出答案。
组合使用效果最佳:
示例1:
问题:小明有5个苹果,吃了2个,妈妈又给了3个,现在有几个?
思考:初始有5个,吃了2个剩3个,妈妈给3个变成6个。
答案:6个
问题:小红有10元,买了3支铅笔每支2元,还剩多少?
思考:这样模型就会按照示例的"思考→答案"格式输出。
实际项目中的技巧:
- 示例要高质量且多样:覆盖典型场景和边界情况
- 示例顺序有影响:相关度高的放后面效果更好
- 零样本CoT:直接加一句"让我们一步步思考",没有示例也有提升
- 大模型更适合CoT:参数越大,CoT效果越明显
【面试题】6. JVM的垃圾收集器中,G1和CMS有什么本质区别?为什么JDK 9之后官方推荐用G1?
【标准答案】核心区别在于内存布局和回收策略:
内存布局:CMS使用传统的分代模型,堆被分成连续的年轻代和老年代。G1把堆划分成多个大小相等的Region(默认2048个),每个Region可以是Eden、Survivor或Old,不再要求连续。
回收策略:CMS只回收老年代,是"尽最大努力"的收集器。G1是"面向局部"的收集器,它跟踪每个Region的垃圾堆积价值,优先回收垃圾最多的Region(Garbage First得名于此),实现可预测的停顿时间。
为什么官方推荐G1:
- 可控的停顿时间:通过-XX:MaxGCPauseMillis可以设置目标停顿时间(默认200ms),G1会动态调整回收策略
- 没有碎片问题:G1有压缩整理能力,CMS是标记清除,会产生大量碎片,极端情况要Full GC
- 更好的并发性:G1的并发标记更高效,且支持混合回收(年轻代+部分老年代)
- JDK演进方向:后续的ZGC、Shenandoah都是G1思想的延续
CMS的两个致命问题:并发模式失败时退化为串行Full GC,停顿时间不可控;内存碎片严重时触发Full GC整理。这两个问题在高流量生产环境中是定时炸弹。
【面试题】7. 向量数据库的核心指标有哪些?为什么说"向量数据库选型要看场景,不是越贵越好"?
【标准答案】向量数据库的核心指标:
1. 查询性能:QPS(每秒查询数)和P99延迟。索引类型(HNSW、IVF、Flat)对性能影响巨大。HNSW查询快但索引慢、内存大;IVF可调精度和速度平衡。
2. 召回率:真正相关的向量被找回的比例。高召回率通常要牺牲查询速度。评估指标有Recall@K、Recall@10等。
3. 索引构建速度:数据量大时,建索引时间可能长达数小时。增量更新能力也很关键。
4. 内存占用:HNSW索引内存消耗大,IVF+PQ可以压缩到原始大小的10%-30%。
5. 持久化和高可用:数据能否持久化、是否支持分布式部署、故障恢复时间。
为什么说选型要看场景:
- 小数据量(<100万向量):FAISS或pgvector足够,没必要上分布式方案
- 追求极致查询速度:Milvus的HNSW索引、Qdrant的HNSW实现
- 内存受限:用IVF+PQ压缩,牺牲一点召回率换内存
- 需要实时写入:Pinecone、Weaviate的增量更新更成熟
- 已有PG技术栈:pgvector无缝集成,运维成本最低
- 多模态检索:支持标量过滤的Milvus更灵活
没有银弹。如果只是内部知识库检索,pgvector可能就够了;如果是对外的高并发服务,Milvus/Qdrant的大规模分布式能力才有价值。
【面试题】8. MySQL中InnoDB的MVCC是如何实现的?为什么说MVCC让读写不冲突,但RR隔离级别下仍可能出现幻读?
【标准答案】MVCC的核心是版本链和Read View:
版本链:每行记录有两个隐藏列——trx_id(最后修改的事务ID)和roll_pointer(指向undo log的指针)。每次更新都会在undo log中记录旧版本,通过roll_pointer串联成版本链。
Read View:事务启动时创建一个快照,记录三个关键信息:
- m_ids:当前活跃事务ID列表
- min_trx_id:活跃事务中最小的ID
- max_trx_id:下一个将分配的事务ID
读取时判断:如果数据版本的trx_id < min_trx_id,说明已提交,可见;如果在m_ids里,说明未提交,不可见;如果trx_id >= max_trx_id,说明是快照之后的新事务,不可见。
为什么读写不冲突?读操作走快照,不拿锁;写操作加排他锁修改最新版本。彼此互不影响。
为什么RR下仍有幻读?MVCC只能解决"快照读"的幻读。如果当前事务中执行了SELECT * FROM t WHERE id > 100 FOR UPDATE(当前读),会加间隙锁。但如果另一个事务插入了一条id=105的记录,当前事务再执行同样的当前读,就能看到这条新记录——这是幻读。
解决办法:InnoDB的RR隔离级别默认开启了Next-Key Lock(记录锁+间隙锁),当前读时锁住范围内的间隙,阻止其他事务插入,从而防止幻读。但纯MVCC无法解决这个问题。
【面试题】9. 设计一个AI Agent的规划模块,让它能够把复杂任务拆解成子任务并执行。你会怎么设计?需要考虑哪些边界情况?
【标准答案】一个完整的AI Agent规划模块需要这几个核心组件:
1. 任务分析器:接收用户输入,判断任务类型(单轮对话、多步骤任务、需要外部工具)。用大模型做意图识别和任务拆解。
2. 任务图构建:把任务拆成DAG(有向无环图),节点是子任务,边是依赖关系。支持串行、并行、条件分支。
3. 执行器:遍历任务图,按拓扑顺序执行。每个子任务可以是LLM调用、工具调用或API请求。
4. 状态管理:维护全局状态(已完成的子任务结果、错误信息),支持断点续跑。
5. 反思与修正:执行失败时,用LLM分析原因,决定重试、跳过还是回滚。
边界情况考虑:
- 任务拆解粒度:太粗执行失败难定位,太细效率低。可以设定"每个子任务不超过3分钟执行时间"的经验规则
- 循环依赖检测:任务图不能有环,建图时要校验
- 超时处理:单个子任务设置超时,避免整个Agent卡住
- 资源限制:工具调用可能有速率限制,需要限流和重试策略
- 错误传播:子任务失败时,下游依赖它的任务如何处理?是跳过、用默认值还是终止整个流程
- 日志与可观测性:每个决策节点要记录原因,方便调试和审计
技术选型:LangGraph适合有复杂状态机的场景,LlamaIndex适合RAG密集型Agent。如果追求灵活可控,自己用DAG框架+LLM封装也不复杂。
【面试题】10. Redis的分布式锁用SET NX PX实现有什么问题?Redlock算法解决了什么问题?生产环境你推荐怎么用?
【标准答案】SET NX PX的基本用法:
SET lock_key unique_value NX PX 30000NX表示key不存在才设置,PX设置30秒过期。释放锁时用Lua脚本保证原子性:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
end
return 0存在的问题:
- 单点故障:Redis挂了,锁就没了
- 时钟漂移:过期时间依赖本地时钟,多节点时钟不同步会导致锁意外释放
- 锁续期:业务执行超过过期时间,锁自动释放,其他线程拿到锁后,原来的线程执行完释放了别人的锁
- 主从切换:客户端A在Master拿到锁,还没同步到Slave,Master挂了,Slave升级后B也能拿到同一把锁
Redlock算法:在多个独立的Redis节点上获取锁(通常5个),只有超过半数节点获取成功才算成功。解决了单点故障和主从切换问题。
生产环境推荐:
- 简单场景:单Redis实例 + SET NX PX + 看门狗(锁续期线程)足够。大部分业务不需要Redlock的复杂性
- 高可用要求:用Redisson框架,内置看门狗、Lua释放、可重入锁支持,一行代码搞定
- 极端可靠性:如果业务不能容忍任何锁失效风险,用Zookeeper或etcd,它们的一致性保证比Redis强
- 避免长事务:锁持有时间尽量短,业务逻辑放到锁外面预处理
关键经验:分布式锁是trade-off,不是越强越好。Redlock增加了复杂度和延迟,只有在对一致性要求极高的场景才值得。多数业务用Redisson的默认实现就够了。