Java实习模拟面试之同盾科技:深入并发、分布式与MySQL索引的连环拷问
在准备 Java 后端开发实习岗位的过程中,我模拟了一场来自同盾科技(国内知名智能风控与安全科技公司)的技术面试。这场面试不仅考察了基础编码能力,更聚焦于高并发、分布式系统、JDK 并发工具、MySQL 索引原理等核心知识点,并通过连环追问的方式深入挖掘候选人的技术深度。
本文将以真实对话形式还原这场“一面 + 二面”的模拟面试全过程,帮助大家更好地理解面试官的考察重点和回答思路。
一面:夯实基础,聚焦并发与存储
1. 面试官提问:请做个自我介绍。
我:
好的,面试官您好!我是 XX 大学计算机专业的大三学生,目前主攻 Java 后端开发方向。在校期间参与过两个中型项目,主要使用 Spring Boot + MyBatis + MySQL 技术栈,对多线程、数据库优化和分布式有一定了解。最近也在系统学习 JVM 和并发编程相关内容。希望能有机会加入同盾科技,在实际业务场景中提升工程能力。
2. 面试官提问:能详细介绍一下你做的项目吗?
我:
当然。我做过一个校园二手交易平台,后端采用 Spring Boot 构建,前端是 Vue。核心功能包括商品发布、订单管理、消息通知等。为了提升性能,我引入了 Redis 缓存热点数据(比如商品详情),并用 RabbitMQ 解耦下单和库存扣减逻辑。面试官追问:那如果用户同时下单同一商品,库存会不会超卖?
我:
这个问题我们确实考虑过。在扣减库存时,我们使用了数据库乐观锁(version 字段)+Redis 分布式锁双重保障。先尝试获取 Redis 锁,成功后再执行 SQL:UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = ? AND stock > 0 AND version = ?。这样即使高并发也能保证一致性。
3. 面试官提问:你们项目里有没有做分布式数据同步?如何保证数据一致性?
我:
在订单服务和库存服务分离的场景下,我们通过可靠消息最终一致性来实现。具体来说:
- 下单成功后,向 RabbitMQ 发送“扣库存”消息,并将消息状态持久化到本地消息表;
- 库存服务消费消息后执行扣减,并回写确认状态;
- 如果消费失败,会有定时任务扫描未确认消息进行重试。
这样虽然不能强一致,但能保证最终一致性,且避免了分布式事务的复杂性。
面试官追问:那如果 MQ 挂了怎么办?
我:
我们的消息表会记录待发送状态,即使 MQ 不可用,也可以通过补偿机制(比如定时任务)重新投递,确保不丢消息。
4. 面试官提问:项目中 JDK 版本用的是多少?
我:
我们用的是JDK 1.8,主要是因为它的 Lambda 表达式、Stream API 能提升开发效率,而且生产环境兼容性好,社区支持成熟。
5. 面试官提问:JDK 1.8 的并发包你了解哪些?重点说说你用过的。
我:
我主要用过java.util.concurrent包里的几个核心类:
ConcurrentHashMap:线程安全的哈希表,JDK 1.8 改为synchronized + CAS,性能比 Hashtable 高很多;CountDownLatch:用于主线程等待多个子任务完成,比如并行查询多个接口后汇总结果;CompletableFuture:异步编排利器,可以链式调用、异常处理,比传统 Future 更灵活。面试官追问:ConcurrentHashMap 为什么不用 ReentrantLock 了?
我:
JDK 1.7 用的是分段锁(Segment + ReentrantLock),但锁粒度还是偏大。1.8 改成对每个桶头节点加 synchronized,配合 CAS 插入,既减少了内存开销,又提升了并发度——因为 synchronized 在 JVM 层做了大量优化(偏向锁、轻量级锁等)。
6. 面试官提问:线程池你是怎么使用的?参数怎么设置?
我:
我们项目里自定义了线程池,没有直接用Executors,因为容易导致 OOM。示例配置:
newThreadPoolExecutor(2,// corePoolSize4,// maximumPoolSize60L,TimeUnit.SECONDS,newLinkedBlockingQueue<>(100),newThreadFactoryBuilder().setNameFormat("biz-pool-%d").build(),newThreadPoolExecutor.CallerRunsPolicy());核心原则:
- CPU 密集型任务:corePoolSize ≈ CPU 核数;
- IO 密集型:可适当增大,比如 2 * CPU 核数;
- 拒绝策略选 CallerRunsPolicy,避免任务丢失,同时起到“反压”作用。
7. 面试官提问:MySQL 索引你了解吗?什么时候会失效?
我:
索引本质是B+树结构,能加速查询。常见失效场景包括:
- 对字段使用函数或表达式,如
WHERE YEAR(create_time) = 2025;- 左模糊查询
LIKE '%abc';- 类型隐式转换,比如字符串字段传数字;
- 复合索引不满足最左前缀原则。
我们项目里通过
EXPLAIN分析执行计划,确保走索引。
8. 面试官提问:B 树和 B+ 树有什么区别?
我:
主要三点区别:
- 数据存储位置:B 树的非叶子节点也存数据,B+ 树只在叶子节点存数据;
- 叶子节点连接:B+ 树的叶子节点用双向链表连接,适合范围查询;
- 扇出更高:B+ 树非叶子节点只存 key 和指针,单页能存更多索引项,树更矮,IO 更少。
所以 MySQL 选择 B+ 树作为索引底层结构。
9. 面试官提问:截至目前,你做过最满意的一件事是什么?
我:
是在课程设计中独立实现了一个分布式 ID 生成器,参考了雪花算法(Snowflake),但加入了ZooKeeper 动态分配 workerId,避免节点冲突。还写了单元测试和压力测试,QPS 达到 5w+。这个过程让我深入理解了分布式系统中的唯一性、时钟回拨等问题。
10. 面试官提问:手撕算法——反转链表(LeetCode 206)
我:(白板写代码)
publicListNodereverseList(ListNodehead){ListNodeprev=null;ListNodecurr=head;while(curr!=null){ListNodenext=curr.next;curr.next=prev;prev=curr;curr=next;}returnprev;}时间复杂度 O(n),空间 O(1)。也可以用递归,但要注意栈溢出风险。
二面:深入系统,考察架构思维
1 & 2. 自我介绍 + 项目回顾(略,与一面类似)
3. 面试官提问:项目中有没有涉及多线程和分布式的内容?具体说说。
我:
有的。除了前面提到的分布式锁和消息队列,我们在日志收集模块用了多线程:
- 主线程接收日志请求,放入阻塞队列;
- 多个工作线程从队列取日志批量写入 ES,提升吞吐。
另外,我们用 Nginx 做负载均衡,部署了两个订单服务实例,属于简单的分布式架构。
4. 面试官提问:GC 机制了解吗?说说 G1 和 CMS 的区别。
我:
JVM 的 GC 主要回收堆内存。CMS(Concurrent Mark Sweep)是以低延迟为目标的老年代收集器,但存在碎片问题,且 JDK 9 后已废弃。G1(Garbage First)是面向服务端应用的收集器,它把堆划分为多个 Region,优先回收垃圾最多的 Region(“Garbage First”)。优点包括:
- 可预测停顿时间(通过
-XX:MaxGCPauseMillis设置);- 有压缩过程,避免碎片;
- 并发标记阶段与用户线程并行。
现在主流推荐使用 G1 或 ZGC(JDK 11+)。
5. 面试官提问:如果线上服务内存占用很高,你怎么排查?
我:
我会按以下步骤排查:
- top / htop查看进程 RES 内存;
- jstat -gc观察老年代是否持续增长;
- jmap -histo:live查看对象数量分布;
- dump 内存快照:
jmap -dump:format=b,file=heap.hprof <pid>;- 用MAT(Memory Analyzer)分析 dominator tree,找内存泄漏点(比如静态集合不断 add 对象)。
常见原因:缓存未设 TTL、ThreadLocal 未清理、大对象频繁创建等。
总结:同盾科技面试的核心考察点
通过这场模拟面试可以看出,同盾科技对 Java 实习生的要求不仅限于 CRUD,而是重点关注:
- ✅并发编程能力(线程池、JUC 工具、锁机制)
- ✅数据库底层原理(B+树、索引失效、事务)
- ✅分布式系统思维(一致性、消息可靠性、ID 生成)
- ✅JVM 与性能调优意识(GC、内存分析)
- ✅动手能力(算法 + 项目细节)
建议大家在准备实习面试时,不要只背八股文,而要结合项目讲清楚“为什么这么设计”、“遇到什么问题”、“如何权衡取舍”。这才是打动面试官的关键。
欢迎关注我的 CSDN 主页,后续将持续更新大厂模拟面试系列!
👉 如果你觉得本文对你有帮助,别忘了点赞 + 收藏 + 转发!