蚂蚁集团 AI应用开发 一面
1. 做一下自我介绍
2. Java 里线程的工作原理,不从 API 角度回答,说一说
线程本质上是操作系统调度的执行单元,在 JVM 里对应一条独立的执行路径。面试里往深一点讲,重点不是 Thread 类怎么用,而是线程和 CPU 时间片、用户态内核态切换、上下文切换成本、工作内存与主内存可见性之间的关系。Java 程序看上去是多个线程在同时执行,真正底层依赖的是操作系统调度器、CPU cache、一致性协议和 JVM 对内存模型的约束。很多并发 bug 并不是“线程不安全”这四个字能概括,而是可见性、原子性和有序性没有同时满足。
3. volatile 到底解决了什么问题,为什么它不是轻量版 synchronized
volatile 解决的是可见性和一定程度上的有序性,不解决复合操作的原子性。也就是说,一个线程修改了被 volatile 修饰的变量,其他线程能尽快看到这个新值,同时 JVM 和 CPU 不会在它周围随意重排关键指令。但像 count++ 这种读-改-写复合操作,哪怕变量是 volatile,多个线程同时执行仍然会丢更新。所以它不是轻量版锁,而更像一种内存语义声明,适合做状态标志、配置刷新和双重检查中的发布控制。
class Switcher {
private volatile boolean stopped = false;
public void stop() {
stopped = true;
}
public void run() {
while (!stopped) {
// do work
}
}
}
4. Lock 锁和 synchronized 真正的差异是什么,为什么有时候明明能用 synchronized 却还要用 Lock
synchronized 是 JVM 原生监视器语义,使用简单,自动释放,适合边界清晰的互斥场景。Lock 则更像可编程的同步器,支持可中断获取、公平策略、超时获取、条件队列和更灵活的组合控制。真正需要 Lock 的场景,往往不是“它性能更高”,而是你需要显式管理竞争行为,比如限时获取锁、多个条件等待队列、或者想在复杂状态机里把加锁和解锁位置拆开。很多高级并发结构最终都不是靠 synchronized 搞定,而是靠 AQS 这类框架抽象出来的同步器。
5. AQS 为什么能成为很多并发组件的底座,它真正抽象了什么
AQS 抽象的不是“锁”本身,而是一个基于状态值和 FIFO 等待队列的同步框架。它用一个 state 表示同步状态,再配合 CAS、入队、阻塞、唤醒,统一承载独占和共享两种获取模式。像 ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 都是在这个框架上实现的。AQS 的价值在于把复杂同步逻辑收敛成“尝试获取失败就排队,状态满足后再唤醒重试”的模型,开发者只需要定义状态如何变化,而不用每次都从零手写并发控制。
6. Spring Bean 生命周期如果从容器角度解释,哪些阶段最关键
从容器视角看,Bean 生命周期关键阶段是实例化、属性填充、Aware 回调、前置后置处理器、初始化、使用和销毁。真正面试里更值得讲的是两个扩展点:一个是 BeanPostProcessor,它能在初始化前后介入 Bean,很多代理增强都发生在这里;另一个是循环依赖和提前暴露对象的问题,尤其是单例 Bean 在三级缓存中的处理。理解生命周期,不只是为了背顺序,而是为了知道 AOP、事务、依赖注入和代理对象到底是在什么时机产生的。
7. AOP 原理,别只说动态代理
AOP 底层确实依赖动态代理,但如果只说 JDK 代理和 CGLIB,就太浅了。真正要讲的是容器如何在 Bean 创建过程中识别切面、解析切点、构造 Advisor,再决定对哪些 Bean 创建代理对象。最终业务方法执行时,调用会进入拦截器链,事务、日志、鉴权、限流这些横切逻辑按顺序织入。AOP 不是一个“代理技术点”,而是容器、元数据和责任链一起配合的结果。
@Around("execution(* com.demo.service..*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
System.out.println("cost=" + (System.currentTimeMillis() - start));
}
}
8. 除了动态代理,线上系统里你还常见哪些设计模式真的有用
真正常见的不是八股里那些“工厂、单例”简单点名,而是模式如何支撑复杂系统演进。比如责任链很适合做请求校验、风控规则、网关过滤器和模型拦截器;模板方法适合统一任务执行框架;策略模式适合优惠、路由、调度和检索算法切换;观察者或发布订阅适合事件驱动链路;状态模式很适合订单流转和审核流程。工程里模式的意义不是答题,而是把变化隔离开,让复杂逻辑不会堆成一个巨型 if-else。
9. 在分布式系统里,A 调 B 时如果 B 异常或者网络抖动,调用方应该怎么处理才算完整
完整处理一定不是“catch 异常然后重试”。先要区分是超时、连接失败、业务失败还是部分成功,因为它们的补救策略完全不同。然后再看请求是否幂等,是否允许重试,重试用固定间隔还是退避策略,是否要做熔断和降级。更难的是处理“调用方没收到成功响应,但下游其实已经成功”的场景,这时候如果没有幂等键和结果查询接口,重试反而可能制造重复副作用。真正成熟的调用治理,核心是失败分类、幂等语义和可观测性。
10. 时序一致性或者幂等控制在订单类系统里一般怎么做
订单系统里幂等不能靠“前端别点两次”这种假设。比较稳的做法是业务请求带幂等键,服务端用唯一约束、幂等表或者状态机收敛重复请求。时序一致性更复杂,因为你可能会遇到“支付成功先到,订单创建确认后到”这种乱序事件,所以核心不是要求所有事件天然有序,而是让状态转移只接受合法前驱。也就是说,不合法的重复、回退和越级迁移都要被拒绝或者忽略。
create table order_idempotent (
biz_key varchar(64) primary key,
order_id bigint not null,
create_time datetime not null
);
11. 多系统下单和支付要保证最终事务一致性,通常怎么落地
分布式事务在线上真正常用的不是强一致两阶段提交,而是最终一致的事件驱动方案。典型做法是订单服务先落本地事务,再通过事务消息或本地消息表把支付事件可靠送出去,支付完成后回写订单状态。整个过程中每一步都要幂等,而且补偿逻辑不能和主逻辑混在一起。最终一致的关键不在“绝不出错”,而在“即使中间某步失败,也总能通过重试和补偿收敛到正确状态”。
12. 用 Kafka 的理由如果不能只说吞吐高,你会怎么答
Kafka 的优势不只是吞吐大,而是它非常适合日志型、事件型、可回放的流式场景。它通过顺序追加写、分区并行、批量传输、页缓存和零拷贝把吞吐做上去,同时又天然支持消费位点、重放和多订阅者模型。和很多传统 MQ 相比,它更像一个分布式提交日志,而不是纯消息投递系统。所以如果场景是实时分析、事件总线、CDC、埋点流和异步解耦,Kafka 会非常顺手;但如果你要的是复杂延迟消息、优先级、严格事务隔离,未必最优。
13. Kafka 为什么能做到高吞吐,瓶颈通常又会出现在哪里
高吞吐来自几个关键点:顺序写磁盘、批量发送与拉取、分区并行、操作系统页缓存、压缩和零拷贝。它避免了大量随机 IO,把磁盘当成顺序日志设备来使用。但线上瓶颈通常不是某一个魔法点失效,而是 Broker 磁盘打满、分区倾斜、消费跟不上、页缓存命中变差、网络带宽不够或者副本同步延迟。很多人只记得 Kafka 快,却忽略了快的前提是访问模式和资源模型匹配。
14. 追踪一次订单状态变更的链路时,如果中间发生数据丢失,你怎么定位
先确定“丢失”是没生产、没投递、没消费、消费失败未补偿,还是已经处理但查询链路看不到。然后按链路把日志、traceId、消息 key、数据库记录、消费位点和告警时间线串起来。真正难的是异步系统里多个环节都可能是最终一致,看上去像丢数据,实际上只是延迟或视图未更新。所以排
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.
查看7道真题和解析