SHEIN Java 一面:库存一致性、JVM 与 Spring 追问
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 项目的背景是什么,遇到哪些问题,如何解决?
- 项目数据只放在 Redis 中吗,是否还有数据库?
- 如何保证 Redis 与数据库的数据一致性?
- 发生超卖或少卖时,如何处理和补救?
- 一百件库存只卖出九十九件,剩下一件该如何处理和排查?
- 排查超卖、少卖时,应检查哪些环节?
- String、StringBuilder 和 StringBuffer 有什么区别?
- 为什么 String 不可变,StringBuffer 如何提供线程安全?
- == 与 equals 有什么区别?
- Integer 的缓存机制是什么?
- final、finally 与 finalize 分别表示什么?
- Java 泛型如何实现,有哪些限制?
- HashMap 的底层结构是什么,树化和退化为何存在?
- ConcurrentHashMap 如何支持并发访问?
- ThreadLocal 为什么可能导致内存泄漏?
- JVM 运行时数据区有哪些?
- StackOverflowError 与 OutOfMemoryError 有什么区别?
- 垃圾回收有哪些基本算法?
- 双亲委派是什么,为什么这样设计?
- Tomcat 类加载机制与传统双亲委派有哪些不同?
- ZGC 如何减少垃圾回收停顿?
- 线上频繁 Full GC 时怎样排查?
- 使用过哪些 Java 并发机制?
- AQS 的核心原理是什么?
- 公平锁与非公平锁有什么区别?
- Spring IoC 和 AOP 分别是什么?
- 代理有哪些实现方式,Spring 怎样选择?
- @Autowired 与 @Resource 有什么区别?
- Spring Bean 的生命周期是什么?
- 三级缓存解决什么问题,能否解决构造器注入的循环依赖?
- @Transactional 在哪些情况下不能产生预期的事务效果?
- 使用过的 Spring 相关框架版本之间,有哪些升级变化?
- 是否自己编写过 starter?
- InnoDB 和 MyISAM 有什么区别?
- 联合索引 (A,B,C) 如何匹配查询条件?
- MVCC 怎样实现并发读取?
- MySQL 有哪些事务隔离级别,InnoDB 默认使用哪一级?
- 一条 UPDATE 的执行过程是什么?
- 深分页如何优化?
- Redis 有哪些常用数据结构,为什么执行速度快?
- CAP 理论表达了什么限制?
- K8s 多实例部署下如何生成全局唯一 ID?
- UUID 如何生成,除 UUID 外还有哪些方案?
《参考解析》
先说明库存的权威记录在哪里
数据库扣减可以使用带库存条件的原子更新,例如扣一件时要求 stock > 0,并核对受影响行数。订单创建、库存预占、支付确认和超时释放之间,要有明确状态和唯一业务标识。重复消息不能重复扣减,重复取消也不能多放回库存。
少卖时要沿这条链检查是否存在过期未释放的预占、处理失败的补偿、缓存旧值或订单状态不一致。看到还剩一件库存,不应直接把某个数字加一;先确认它属于哪个业务状态。
ThreadLocal 的生命周期往往比一次请求长
线程池会复用线程,放进去的请求上下文可能一直跟随工作线程。ThreadLocalMap 中键的弱引用并不意味着值一定会立即释放;不再使用时,应在 finally 中调用 remove。若值本身很大,长时间保留会加重内存压力;若值属于上个请求,还可能出现上下文串用。
事务注解要经过实际代理入口
在基于代理的事务管理中,同一对象内部直接调用另一个标注事务的方法,通常不会经过代理,因而不会开启那个方法声明的事务。还应检查事务管理器、传播行为和回滚规则。把异常捕获后当成功返回,也可能让事务正常提交,不能只看方法上有没有注解。
Full GC 先看回收前后差了多少
把 GC 日志、堆占用变化与业务负载对起来看。回收后占用仍持续增长,要检查长寿命对象的引用链;回收有效但频率高,则继续检查分配速度、堆容量与晋升压力。分析时保留运行参数和时间线,避免只凭“Full GC 多”就认定是内存泄漏。
深分页可以改为从上一条继续找
按稳定且可索引的键排序,下一页使用上一页末尾的排序键作为查询边界,避免反复跳过大量前序记录。排序字段不唯一时,应加唯一键作为第二排序条件。这个方案适合连续翻页;任意跳到第几页的需求,需要另行权衡。