面灵AI→

用友金融Java后端实习一面:HashMap与MQ幂等

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 实习项目拷打(响应时间是怎么做的优化的)
  2. HashMap 在 JDK 1.8 之后的升级
  3. HashMap 是线程安全的吗?为什么?
  4. 在并发场景之下使用什么?ConcurrentHashMap 为什么能够保证线程安全?
  5. 在实际开发场景之下有没有碰到过 OOM,OOM 主要是由哪些原因造成的?
  6. JVM 的内存区域构成
  7. 怎么保证 RabbitMQ 中的消息不重复消费,怎么保证 MQ 中的消息不丢失?
  8. 排查慢 SQL
  9. MySQL 怎么保证事务一致性?
  10. 开发过程中用得比较多的 AI 工具是什么?具体说说是怎么用的
  11. 有没有听过 skill,开发过程中有没有使用过 skill?
  12. 你对用友集团的了解
  13. 反问

《参考解析》

HashMap 在 JDK 1.8 的升级:底层结构从「数组 + 链表」变成「数组 + 链表 + 红黑树」,当某个桶的链表长度达到 8 且数组容量 ≥ 64 时转成红黑树,退化阈值为 6,把最坏情况下的查询复杂度从 O(n) 降到 O(log n)。插入方式从 1.7 的头插改成尾插,头插在并发扩容时会导致链表成环、进而 CPU 打满,尾插消除了这个问题(但 HashMap 依然不是线程安全的)。哈希计算从 1.7 的「4 次移位异或」简化成 h ^ (h >>> 16),高位参与运算以降低碰撞。扩容机制上,容量始终是 2 的幂(保证 hash & (n-1) 等价取模),1.8 扩容时不需要重新计算 hash,而是用 hash & oldCap 判断元素是留在原位置还是移动到 原位置 + oldCap,把 rehash 拆成两条链表一次遍历完成。默认初始容量 16、负载因子 0.75,树化阈值 8 是泊松分布下极低概率的折中。

HashMap 线程不安全与 ConcurrentHashMap:不安全体现在三处:多线程 put 可能互相覆盖(两个线程同时算出同一个桶,后写的覆盖前者);并发扩容时可能丢数据或读到中间态;1.7 的头插还可能成环。所以并发场景用 ConcurrentHashMap。1.8 的实现放弃了分段锁,改用 CAS + synchronized 锁单个桶头节点:数组为空时用 sizeCtl 做 CAS 初始化;插入空桶用 CAS 直接放;桶非空就 synchronized (f) 锁住头节点再操作链表/红黑树,锁粒度是单个桶,并发度等于桶数量。扩容支持多线程协同(transfer + ForwardingNode 标记,其他线程遇到正在迁移的桶会去帮忙),size 用 baseCount + CounterCell[] 分散计数避免热点。读操作完全无锁,靠 volatile 保证节点可见性(Node 的 val 和 next 是 volatile,1.8 后不保证强一致但保证安全)。要补一句:ConcurrentHashMap 只保证单次操作原子,get 后 put 的复合操作(比如先判断再写入)仍需 computeIfAbsent/putIfAbsent 或额外同步。

OOM 的常见原因:一是堆内存不足(Java heap space)——内存泄漏(静态集合只增不减、监听器未注销、ThreadLocal 未 remove、连接/流未关闭)或瞬时大对象/大数据量查询(一次 select * 拉几百万行、大文件一次性读入、超大数组);二是元空间溢出(Metaspace)——动态生成类过多(CGLIB 代理、热部署、Groovy/脚本引擎反复编译);三是线程过多(unable to create new native thread)——线程池无界创建、每次请求 new Thread;四是 GC 开销超限(GC overhead limit exceeded)——堆几乎耗尽、GC 反复却回收不出空间,本质仍是内存泄漏或堆太小;五是直接内存(Direct buffer memory)——NIO ByteBuf 未释放。排查套路固定:启动加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=...,出问题后抓 dump 用 MAT 看支配树和 GC Roots 引用链,定位到具体持有者;同时看 GC 日志区分是泄漏还是容量不足。

JVM 内存区域:线程私有的三块——程序计数器(当前字节码行号,唯一不会 OOM 的区域)、虚拟机栈(每个方法一个栈帧,存局部变量表、操作数栈、动态链接、返回地址;栈深度超限抛 StackOverflowError)、本地方法栈(Native 方法)。线程共享的两块——堆(对象实例与数组,分新生代 Eden + 两个 Survivor 和老年代,是 GC 主战场)、方法区(1.8 起由元空间实现,存在本地内存,存类元信息、运行时常量池、静态变量)。此外还有直接内存(NIO 的 DirectByteBuffer 走本地内存,不受 -Xmx 限制但受物理内存限制)。要能说出各区域对应的异常:堆 OOM、栈 StackOverflowError、元空间 OOM、直接内存 OOM。

RabbitMQ 不重复消费与不丢失:先说清 MQ 的默认语义是「至少一次」,所以不重复消费靠消费端幂等,不靠 MQ。幂等实现有四种常用手段:业务唯一键 + 数据库唯一索引(插入冲突即视为已处理)、去重表/Redis SETNX 记录消息 ID 并设 TTL、状态机前置校验(只有处于待处理状态才允许流转)、以及乐观锁版本号。注意 ack 时机决定重复范围:处理完业务再 ack 最多重复一次,先 ack 后处理则可能丢消息,通常选前者。不丢失要分三段保:生产端用 publisher confirm(异步确认 + 失败重发)和 mandatory/return 回调确认路由成功,必要时配合本地消息表保证「业务成功则消息必发」;broker 端声明 durable 队列 + persistent 消息,并用镜像队列或 quorum 队列防单点故障;消费端用手动 ack,业务异常时 nack 重入队(要防死循环,配重试次数与退避),超过次数投递到死信队列(DLX)并告警人工介入。

慢 SQL 排查:开 slow_query_log,用 long_query_time 圈定阈值,用 pt-query-digest 按总耗时排序找最值得优化的语句;拿到语句先 EXPLAIN(或 EXPLAIN ANALYZE)看 type、key、rows、Extra,重点识别全表扫描 ALL、Using filesort、Using temporary、以及预估行数与实际差异大导致的索引选错。常见整改方向:按最左前缀建联合索引并尽量做成覆盖索引;避免对索引列做函数/运算和隐式类型转换(字符串列传数字、LIKE '%x'、OR 跨列);大偏移分页改游标或延迟关联;子查询改 JOIN;SELECT 收敛字段避免回表;把统计类查询异步化或搬到离线/缓存。改完要回归执行计划与 Handler_read_*,并评估新增索引带来的写放大。

MySQL 怎么保证事务一致性:靠「日志 + 锁 + 隔离级别」三件套。原子性和持久性由 redo log 保证:修改先写 redo(WAL),事务提交时 innodb_flush_log_at_trx_commit=1 才刷盘,崩溃后按 redo 前滚恢复已提交事务。一致性由 undo log 保证:回滚时按 undo 反向恢复,同时 undo 还承担 MVCC 的历史版本(配合 ReadView 实现一致性快照读)。隔离性由锁和 MVCC 保证:行锁、间隙锁/Next-Key Lock 防幻读、RR 下快照读不加锁。两阶段提交解决 redo 与 binlog 的一致性(prepare → 写 binlog → commit),保证主从复制和崩溃恢复的数据一致。答完要补实践注意点:事务要短小、避免大事务(锁持有久、undo 膨胀、主从延迟大)、避免在事务里做远程调用、RR 与 RC 的选择要结合业务(RC 减少间隙锁、并发更好,但需要 binlog 用 row 格式)。

AI 工具与 Skill:如实回答你的使用方式,重点讲工作流而不是工具名。可以分三类:写代码(样板逻辑、单测、SQL 改写、正则、脚本)、理解代码(读老项目、解释报错栈、生成文档与注释)、以及提效(生成测试数据、写 commit message、做 code review 初筛)。同时给边界:核心业务逻辑、并发与事务、SQL 与鉴权自己复核;不把公司和客户代码贴到不合规的公有工具上。如果被问到 Skill,可以理解为把重复的团队流程封装成可复用的提示词/工具组合(比如固定格式的代码审查、固定模板的单测生成),并说明你用过或自己写过哪一类,以及它带来的实际收益(省了多少时间、减少了多少遗漏)。

对用友的了解:这类问题要提前做功课,落到具体业务而不是泛泛夸。可用的事实框架:用友是国内老牌的企业管理软件与云服务厂商,核心产品线覆盖大型企业的 YonBIP 商业创新平台、成长型企业的 YonSuite、以及面向中小企业的畅捷通 T+ 等,主打财务、人力、供应链、采购、制造的数字化。用友金融是面向金融行业的子公司/业务板块,做的是银行、保险、证券等金融机构的管理与业务系统。回答时再结合自己的经历说一句「我在实习里做的是 XX,和贵司的 XX 方向比较对口」,比背公司简介有说服力。

反问:实习岗适合问「实习生具体参与哪些模块、有没有导师带、能不能接触到真实线上系统」以及「转正机制和时间点」。技术面也可以问团队的技术栈(是自研框架还是主流 Spring 生态)、以及实习生的工作范围是否会涉及加班较多的交付期。