蚂蚁集团 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、入队、阻塞、唤醒,统一承载独占和共享两种获取模式。像 ReentrantLockSemaphoreCountDownLatchReentrantReadWriteLock 都是在这个框架上实现的。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面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论

相关推荐

tl: 4.4一面,面完秒转二面4.7约二面4.8二面,面完一小时转HR面,HR加微信约面4.9HR面4.14无oc直接offer,中间五天等待的时候很焦虑,拿到offer全都大雪深埋了。聊聊一路的心理历程吧,面试的时候在vivo日常在职,所以整体下来一直在安慰自己哪怕挂了也可以在这边实习到暑期,压力可能没有想象中那么大,全程面的比较放松。双非本,两段小厂实习->vivo->鹅厂,整个暑期只有鹅约面了,其他的全是笔试挂简历挂,不由得感叹双非本确实在后端这边机会不是很多,vivo日常这边的几个同事也都是985硕。暑期从一月底才有意识开始准备,当时投了很多中大厂的日常,全都毫无意外的挂了,面了商汤记得被拷打了,当时痛定思痛打算恶补基础和手撕,当天晚上刷别人成功的经验视频到早上去上班一直没睡着觉,写下了p1的规划计划,在此之前就简单写完了外卖和点评和微服务对于八股没有一点概念,每天八点起床,坐半小时公交上班,五点半下班,坐半小时回家,公交车上和到家吃完饭洗完澡就开始猛看黑马的MySQL,Redis,JUC,JVM,一个月时间把这四大件全看完了,力扣hot100也在这一个月过完了第一遍,p2是当时睡前截的图记录看的进度,每天都很疲惫但是睡不着觉害怕自己竞争不过92的对手,不得不说虽然是个小国企,但是这段实习的mentor人很好,教会我很多东西,在他影响下买了好几本机械工业出版社的书,现在也偶尔会看一看,感恩。整个三月都在焦虑,三月初面了字节今日头条项目组,被拷打一个小时全程声音在颤抖,面试官最后说基础不牢就给秒挂了,于是害怕自己基础不牢直到三月下旬才开始投递,每天逼着自己看暑期的新开资讯,看进度,看完进度发现一直在初筛,刷到同城28届拿到鹅厂实习,牛客小红书上全是同龄人的offer每天都在自我怀疑。四月面了几家中厂(面着玩),发现自己相较于第一次面试已经顺畅非常多,手撕也比之前有头绪,项目也能融会贯通觉得是时候冲击大厂,但是一直没有约面,到清明那周的周一收到鹅的约面约了4.4一面,面试前一天把codetop做了五页,一觉睡到下午背了两小时八股,坐在面试连接前给自己心理暗示了一个小时开始面试,出乎意料的很顺,全程一小时无卡点手撕虽然是hard但是撕出来了。面完在房间挥拳庆祝,特别兴奋,之前根本就没有过过一面。4.7清明回来上班第一天收到二面约面电话,拒了当天晚上约了第二天中午,一面很顺所以二面是带着憧憬去的。4.8中午二面面了一个半小时,项目没问穿但是问了一些还没开发的部分我会如何设计,心里没底因为面试前没准备,感觉像压力面所以把情绪调整好就开始思考,感觉表现还不错但是有挺多设计层面的东西没考虑全面有点悬,面完一小时看进度到了HR面,很激动,当天下午HR来加wx填云证约面觉得有点小稳,因为看别人中间流程很慢我约面很快。4.9HR面十几分钟就结束了,感觉没什么问题。下班以后在公司楼下逛了一个多小时才回家,感觉自从高考失利后散去所有的少年气全都被春风吹回了身体里。4.9-4.14中间隔了个周末,周一催,周二offer,有过自我怀疑,甚至都释怀了准备投滴滴的时候手机收到短信说offer在邮箱了,确认了好几遍不敢相信,收到offer的时候刚好在看斗破苍穹萧炎九转成圣的片段,眼泪差点没绷住。感觉都值了,总体下来就一句话吧,"别让梦想埋没",感谢一直不愿意放弃自己,希望牛友们坚定自己的信念,请务必一直相信自己,我们能做到也必然会做到。
从投递到OC,你用了多久
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务