Java 大厂一面模拟:从线程池调优到 Spring 事务传播的深度追问

张开发
2026/4/10 15:18:37 15 分钟阅读

分享文章

Java 大厂一面模拟:从线程池调优到 Spring 事务传播的深度追问
面试背景这是一次面向 1-3 年 Java 后端候选人的大厂一面模拟时长约 30 分钟。候选人背景为电商订单系统开发熟悉 Spring Boot、MySQL、Redis、Kafka有基础并发编程经验。面试官风格偏实战注重原理理解与线上落地能力提问节奏紧凑强调“为什么这么设计”和“边界场景怎么处理”。整场面试围绕 Java 并发、Spring 事务、MySQL 锁机制、线程池调优和分布式场景展开重点考察候选人对技术点的理解深度、边界判断能力以及线上问题应对思路。主问题部分问题 1你项目中线程池是怎么配置的核心参数怎么定的候选人回答 我们用的是ThreadPoolExecutor核心线程数设为 CPU 核数的 2 倍最大线程数设为 4 倍队列用LinkedBlockingQueue容量 1000拒绝策略是CallerRunsPolicy。面试官追问为什么核心线程数是 CPU 核数的 2 倍有没有压测数据支撑如果任务主要是 IO 密集型这个设置合理吗候选人回答我们任务主要是查 DB 和调 RPCIO 占比高所以设成 2 倍。压测时 QPS 能到 3000CPU 使用率在 60% 左右没出现明显瓶颈。面试官追问那如果突然流量翻倍队列积压到 800你怎么办有没有监控和报警候选人回答我们有 Prometheus Grafana 监控队列大小和活跃线程数超过阈值会发告警。如果积压严重会临时扩容机器或降级非核心功能。问题 2Spring 事务传播机制中REQUIRES_NEW和NESTED有什么区别候选人回答REQUIRES_NEW会挂起当前事务开启一个新事务NESTED是在当前事务内创建一个保存点子事务回滚不会影响外层事务。面试官追问那如果外层事务回滚了NESTED的子事务会回滚吗候选人回答会的因为NESTED本质上还是同一个物理事务只是通过保存点实现部分回滚。面试官追问那在 MySQL 中保存点是怎么实现的会不会有性能开销候选人回答保存点是通过SAVEPOINT语句实现的MySQL 会记录事务状态。频繁使用保存点可能会增加 undo log 的大小影响性能。问题 3你在项目中遇到过死锁吗怎么排查的候选人回答遇到过一次是两张表互相更新导致的。我们用SHOW ENGINE INNODB STATUS查到了死锁日志发现是事务 A 先锁了表 A再锁表 B事务 B 先锁表 B再锁表 A形成了循环等待。面试官追问那你怎么解决的有没有考虑过用乐观锁替代候选人回答我们调整了 SQL 执行顺序保证所有事务都按相同顺序加锁。也评估过乐观锁但因为并发高版本冲突频繁最终没采用。面试官追问那如果必须用乐观锁你怎么设计重试机制候选人回答可以在应用层做重试比如最多重试 3 次每次加随机延迟避免雪崩。问题 4Redis 做分布式锁你用的是什么方案候选人回答用的是 Redisson 的RLock支持可重入、自动续期、看门狗机制。面试官追问看门狗是怎么工作的如果主节点挂了从节点还没同步锁信息会不会出现多个客户端同时持有锁候选人回答看门狗会定期续期锁的 TTL防止业务执行时间过长导致锁过期。Redis 主从异步复制确实可能导致锁失效所以我们要求业务尽量短或者用 RedLock 算法。面试官追问RedLock 真的安全吗有没有可能因为时钟漂移导致锁失效候选人回答RedLock 依赖多个独立 Redis 实例要求大多数节点成功才算加锁成功。但确实存在时钟漂移风险所以我们更倾向于用 ZooKeeper 或 etcd 做强一致锁。问题 5MySQL 的SELECT ... FOR UPDATE会加什么锁候选人回答会对查询到的行加排他锁X 锁如果走了索引只锁索引记录如果没走索引会锁全表。面试官追问那如果 where 条件是id 100会锁哪些范围候选人回答会锁满足id 100的所有记录以及它们之间的间隙gap lock防止幻读。面试官追问那在 RR 隔离级别下这个锁会持续到什么时候候选人回答会持续到事务结束包括 commit 或 rollback。问题 6你项目中怎么保证消息消费的幂等性候选人回答我们在消费端做了幂等校验比如用 Redis 记录消息 ID消费前先查是否已处理。面试官追问如果 Redis 挂了怎么办有没有考虑过用数据库唯一索引候选人回答我们也有数据库兜底比如订单状态变更表有唯一键重复消费会抛异常我们捕获后直接返回成功。面试官追问那如果消息乱序到达比如“支付成功”消息比“创建订单”还早你怎么处理候选人回答我们会在消费端做状态机校验比如只有“已创建”状态才能进入“已支付”否则拒绝或延迟处理。问题 7Spring Bean 的生命周期中AOP 代理是在哪个阶段创建的候选人回答是在BeanPostProcessor的postProcessAfterInitialization阶段通过AbstractAutoProxyCreator创建代理对象。面试官追问那如果 Bean A 依赖 Bean B而 Bean B 被 AOP 代理了Spring 怎么保证注入的是代理对象候选人回答Spring 在依赖注入时会从三级缓存中获取早期暴露的对象如果该对象需要代理会提前创建代理并放入缓存。面试官追问那三级缓存具体是哪三级为什么需要三级而不是两级候选人回答一级是成品 Bean二级是早期暴露的 Bean三级是 ObjectFactory。三级是为了处理循环依赖时能延迟生成代理对象避免重复创建。问题 8你项目中怎么处理慢 SQL候选人回答我们开启了慢查询日志阈值设成 1 秒定期分析。也用了EXPLAIN看执行计划优化索引。面试官追问那如果EXPLAIN显示typeALL你怎么办候选人回答说明是全表扫描我们会检查 where 条件字段是否有索引或者是否走了最左前缀。面试官追问那如果字段有索引但EXPLAIN还是显示typeALL可能是什么原因候选人回答可能是索引选择性差或者统计信息过期或者查询条件用了函数导致索引失效。追问部分重点追问 1线程池的拒绝策略除了CallerRunsPolicy你还用过哪些为什么选这个候选人回答还用过AbortPolicy直接抛异常。CallerRunsPolicy的好处是让调用线程自己执行任务能起到限流作用避免任务丢失。面试官追问那如果调用线程是 Tomcat 的请求线程会不会影响接口响应候选人回答会的所以我们只在非核心路径用比如日志上报、统计等。核心业务我们会用DiscardPolicy或自定义策略记录日志后丢弃。面试官追问那有没有考虑过用有界队列 动态线程池比如根据负载自动调整核心线程数候选人回答有调研过比如用ThreadPoolTaskExecutor配合监控动态调用setCorePoolSize但实现复杂目前还是固定配置。重点追问 2Spring 事务中如果方法 A 调用方法 BB 抛异常A 捕获了事务会回滚吗候选人回答如果 B 是REQUIRED传播异常会传播到 A即使 A 捕获了事务也会回滚除非用Transactional(noRollbackFor...)。面试官追问那如果 B 是REQUIRES_NEWA 捕获了异常B 的事务会回滚吗候选人回答会的B 的事务独立异常会导致 B 回滚但 A 的事务不受影响。面试官追问那如果 B 的事务回滚了但 A 继续执行并 commit会不会导致数据不一致候选人回答会的所以我们通常会在 A 中检查 B 的执行结果或者用补偿机制。重点追问 3Redis 分布式锁的自动续期如果业务执行时间超过锁的 TTL会发生什么候选人回答看门狗会续期但如果业务线程阻塞比如 Full GC看门狗可能无法续期锁会过期其他客户端可能获取到锁。面试官追问那你怎么避免这种情况候选人回答我们会设置合理的 TTL比如 30 秒并监控 GC 情况。如果业务复杂会拆分成多个小任务。面试官追问那如果看门狗续期失败但业务还在执行怎么办候选人回答我们会在业务逻辑中加状态检查比如用 Redis 记录执行状态避免重复执行。面试点评本场面试主要考察候选人在高并发、分布式场景下的技术深度与实战能力。重点包括线程池调优不仅要知道参数含义还要理解 IO/CPU 密集型任务的区别以及监控与动态调整。Spring 事务传播要求理解REQUIRES_NEW与NESTED的底层差异以及 MySQL 保存点的实现。死锁排查强调从现象到根因的分析能力以及解决方案的权衡。分布式锁考察对 Redis 主从复制、RedLock、时钟漂移等边界问题的理解。消息幂等要求设计完整的防重机制包括 Redis、数据库、状态机等多层保障。候选人容易卡在“原理与线上落地的衔接”上比如知道NESTED是保存点但不知道 MySQL 如何实现知道看门狗续期但没考虑 GC 影响。技术补丁包线程池核心参数调优 原理核心线程数、最大线程数、队列容量、拒绝策略共同决定线程池行为。 设计动机平衡资源利用与任务吞吐量避免 OOM 或线程过多。 边界条件IO 密集型任务可设更高线程数CPU 密集型建议接近核数。 落地建议结合压测数据调整监控队列积压与线程活跃度。Spring 事务传播机制 原理通过TransactionInterceptor和PlatformTransactionManager实现事务边界控制。 设计动机支持嵌套事务、独立事务等复杂业务场景。 边界条件NESTED依赖数据库保存点REQUIRES_NEW会挂起当前事务。 落地建议避免在循环中开启新事务防止连接耗尽。MySQL 死锁排查 原理InnoDB 通过等待图检测死锁自动回滚代价最小的事务。 设计动机保证事务并发执行时的数据一致性。 边界条件死锁日志只保留最近一次需及时采集。 落地建议统一 SQL 执行顺序使用SHOW ENGINE INNODB STATUS分析。Redis 分布式锁实现 原理基于SET key value NX PX timeout实现原子加锁。 设计动机解决多实例部署下的资源竞争问题。 边界条件主从异步复制可能导致锁失效时钟漂移影响 RedLock。 落地建议业务尽量短结合看门狗续期或改用 ZooKeeper。消息消费幂等性设计 原理通过唯一标识消息 ID、业务 ID判断是否已处理。 设计动机防止重复消费导致数据错误。 边界条件Redis 宕机、消息乱序、网络分区等场景。 落地建议多级防重Redis 数据库唯一键 状态机。Spring AOP 代理创建时机 原理在BeanPostProcessor的postProcessAfterInitialization阶段创建。 设计动机支持对 Bean 的方法进行增强。 边界条件循环依赖时需提前暴露代理对象。 落地建议避免在PostConstruct中调用被代理方法。MySQL 行锁与间隙锁 原理FOR UPDATE加排他锁RR 隔离级别下加间隙锁防幻读。 设计动机保证事务隔离性。 边界条件未走索引会锁全表范围查询会锁间隙。 落地建议确保 where 条件走索引避免大事务。慢 SQL 排查与优化 原理通过慢查询日志、EXPLAIN、SHOW PROFILES分析执行计划。 设计动机提升数据库查询性能。 边界条件索引失效、统计信息过期、隐式类型转换。 落地建议定期分析慢日志使用覆盖索引避免SELECT *。线程池拒绝策略选择 原理AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。 设计动机应对任务提交速度超过处理能力的情况。 边界条件CallerRunsPolicy可能阻塞调用线程。 落地建议核心业务用AbortPolicy非核心用CallerRunsPolicy。分布式锁的自动续期机制 原理看门狗线程定期续期锁的 TTL。 设计动机防止业务执行时间过长导致锁过期。 边界条件GC 暂停可能导致续期失败。 落地建议设置合理 TTL监控 GC业务拆分。Spring 三级缓存解决循环依赖 原理一级存成品 Bean二级存早期暴露 Bean三级存 ObjectFactory。 设计动机支持 AOP 代理与循环依赖共存。 边界条件只能解决单例、非构造器注入的循环依赖。 落地建议尽量避免循环依赖使用Lazy延迟注入。MySQL 保存点实现机制 原理通过SAVEPOINT记录事务状态ROLLBACK TO回滚到指定点。 设计动机支持部分回滚减少事务回滚代价。 边界条件保存点过多会增加 undo log 大小。 落地建议谨慎使用避免深层嵌套。消息乱序处理策略 原理通过状态机校验消息顺序延迟处理或拒绝非法状态。 设计动机保证业务逻辑的正确性。 边界条件消息延迟、重复、丢失等网络问题。 落地建议设计幂等接口使用消息版本号或时间戳。线程池动态调整策略 原理通过监控指标动态调用setCorePoolSize调整线程数。 设计动机适应流量波动提升资源利用率。 边界条件频繁调整可能导致线程频繁创建销毁。 落地建议结合 Prometheus 自定义 Controller 实现。分布式事务的最终一致性保障 原理通过消息队列 本地事务表 补偿机制实现。 设计动机解决跨服务事务一致性问题。 边界条件消息丢失、重复消费、网络分区。 落地建议使用可靠消息服务设计幂等消费与对账机制。

更多文章