25年10月抖音 Java开发实习生二面
#JAVA##JAVA面经##JAVA内推#
实习期间做过最有成就感或最棘手的需求是啥?
“虽暂无企业实习经历,但在校园二手平台项目中,我主导开发了高并发秒杀模块(类比实习场景):
- 背景:校园活动期间瞬时流量激增(预估500+并发),需防超卖、保系统稳定
- 我的职责:方案设计→编码→压测→上线全流程
- 设计方案:
1️⃣ 流量削峰:RabbitMQ异步队列缓冲请求(设置预热队列防突发)
2️⃣ 库存控制:
• Redis Lua脚本原子扣减(if stock>0 then decr key end)
• 数据库乐观锁二次校验(version字段)
3️⃣ 防刷机制:用户维度限流(Guava RateLimiter 3次/秒)+ 验证码- 成果:JMeter压测1000并发零超卖,接口P99<200ms
- 反思:初期忽略Redis缓存击穿,用布隆过滤器+空值缓存补救。最大收获:高并发设计需‘理论+压测+监控’三位一体,任何环节缺失都可能线上事故。”
用 RocketMQ 时,如何保证消息严格有序?
“项目中使用RabbitMQ,但深入研究过RocketMQ有序消息机制:
✅ 核心方案:
- 队列级有序:同一业务标识(如订单ID)的消息发送到同一MessageQueue
producer.send(msg, new MessageQueueSelector() { @Override public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { Long orderId = (Long) arg; return mqs.get((int)(orderId % mqs.size())); // 按订单ID哈希固定队列 } }, orderId);- 消费端:单线程消费该队列(避免多线程乱序)
⚠️ 踩坑认知:- 若消费者故障转移,新实例需等待原实例释放队列锁,短暂无序(业务层需幂等兜底)
- 对比RabbitMQ:RabbitMQ需为每个订单建独立队列(运维成本高),RocketMQ的队列复用更优雅
态度:技术选型需匹配业务——订单状态流转用有序消息,通知类消息用普通队列提升吞吐。”
看 EXPLAIN 优化慢 SQL,重点关注哪些字段?
“三字段定乾坤:
🔹 type:
- 目标:
ref/range(走索引)- 警惕:
index(全索引扫描)、ALL(全表扫描)→ 立即加索引
🔹 rows:- 预估扫描行数,若>总数据10%需警惕(如10万表扫5万行)
- 结合
filtered看有效过滤率
🔹 Extra:- ✅
Using index:覆盖索引,最优- ⚠️
Using filesort:ORDER BY未走索引 → 加联合索引- ⚠️
Using temporary:GROUP BY临时表 → 优化分组字段索引
实战案例:曾优化‘用户订单查询’,将(user_id)单列索引改为(user_id, create_time),type从index→range,rows从8000→15,耗时从1.2s→40ms。”
Linux查日志定位"timeout"行,命令组合?
“高效组合三式:
1️⃣ 实时追踪:tail -f app.log | grep --color=auto "timeout"(高亮关键词)
2️⃣ 精准定位:grep -n "timeout" app.log | tail -20(显示行号+最后20条)
3️⃣ 上下文关联:grep -B 2 -A 3 "timeout" app.log(显示匹配行前后2/3行,快速定位堆栈)
进阶技巧:
- 大日志文件:
less +/timeout app.log(less内搜索)- 多文件:
grep -r "timeout" /var/log/myapp/ --include="*.log"- 排除干扰:
grep "timeout" app.log | grep -v "connect"(过滤含connect的timeout)”
端口被占用,查进程命令?
“两步精准定位:
1️⃣ 查进程PID:
lsof -i :8080(推荐,输出含进程名/用户)
备选:netstat -tunlp | grep :8080
2️⃣ 处理进程:
kill -9 <PID>(强制终止)
安全操作:先ps -p <PID> -o comm=确认进程名,避免误杀
实战习惯:部署脚本开头加lsof -ti :8080 | xargs kill -9 2>/dev/null,确保端口释放。”
Java 异常体系,运行时异常 vs 受检异常?
“核心分界:是否需显式处理(try-catch/throws)
🔹 运行时异常(RuntimeException子类):
- 例:
NullPointerException(空指针)、IllegalArgumentException(参数非法)、ConcurrentModificationException(遍历中修改集合)- 特点:程序逻辑错误,应编码避免,通常不捕获
🔹 受检异常(Exception子类,非RuntimeException):- 例:
IOException(文件读写)、SQLException(数据库操作)、ClassNotFoundException- 特点:外部环境导致,必须处理(如重试/降级)
项目实践:- 自定义业务异常继承
RuntimeException(如OrderNotFoundException),简化Controller层- 受检异常统一转换:MyBatis的
SQLException→ 封装为ServiceException(含友好提示)”
数据库典型异常及处理?
“两类高频异常实战处理:
⚠️ 唯一键冲突(DuplicateKeyException):
- 场景:用户重复提交注册
- 处理:
try { userMapper.insert(user); } catch (DuplicateKeyException e) { throw new BusinessException("手机号已注册"); // 返回前端友好提示 }⚠️ 连接超时(
TimeoutException):
- 场景:DB主从切换瞬间
- 处理:
• 配置HikariCP:connection-timeout=3000+max-lifetime=1800000
• 全局异常处理器:捕获后返回系统繁忙,请稍后重试+ 企业微信告警
原则:异常分类处理——用户错误友好提示,系统错误快速告警+降级。”
Dubbo 调用异常与容错设计?
“项目用Spring Cloud,但系统学习过Dubbo容错机制:
⚠️ 常见异常:
- 超时:
RpcException(timeout)- 服务不可用:
NoProviderException- 业务异常:自定义异常透传
🛡️ 容错设计:
1️⃣ 超时控制:@Reference(timeout = 3000)+ 重试(retries=2,幂等接口)
2️⃣ 熔断降级:集成Sentinel,QPS>100自动熔断,返回兜底数据
3️⃣ Mock服务:@Reference(mock = "return new MockOrderService()") OrderService orderService;4️⃣ 监控告警:Dubbo Admin监控调用链,异常率>5%触发告警
认知:微服务容错是‘组合拳’——超时+重试+熔断+监控,缺一不可。”
线程池核心参数作用?
“五参数精准控制:
参数 作用 类比 corePoolSize核心线程数(常驻) 公司正式员工 maximumPoolSize最大线程数 正式+临时工上限 workQueue任务队列(有界/无界) 待办任务池 keepAliveTime非核心线程空闲存活时间 临时工下班等待时长 RejectedExecutionHandler拒绝策略 任务溢出处理方案 关键认知:
- 队列选型决定行为:
SynchronousQueue(直接提交,适合短任务)、LinkedBlockingQueue(无界,防OOM需慎用)- 拒绝策略:
CallerRunsPolicy(主线程执行,平滑降级)比AbortPolicy更友好”
如何配置线程池?考虑任务类型吗?
“配置公式 = 任务特性 × 资源约束:
🔹 CPU密集型(如加密计算):
corePoolSize = CPU核心数(避免线程切换开销)
例:4核机器 → 设4,队列用SynchronousQueue
🔹 IO密集型(如HTTP调用):
corePoolSize = CPU核心数 * 2(线程常阻塞,可多开)
例:4核 → 设8,队列用有界队列(ArrayBlockingQueue(100))
🔹 混合型:
- 核心线程=CPU数,最大线程=CPU*2
- 拒绝策略:
ThreadPoolExecutor.CallerRunsPolicy(触发降级)
项目实践:- 异步发短信线程池:IO密集 →
core=8, max=16, queue=100- 压测验证:调整后CPU利用率稳定在70%,无OOM”
setNx 分段锁设计?为何分段?
“背景:单商品库存扣减,高并发下单个Redis锁成为瓶颈
分段方案:
1️⃣ 分段依据:按商品ID哈希分16段(segment = productId.hashCode() & 15)
2️⃣ 锁Key设计:lock:product:segment:{segmentId}
3️⃣ 流程:
- 扣减前:
SET lock:product:segment:3 nx ex 10(10秒过期)- 执行库存操作
- 释放锁(Lua脚本防误删)
价值:- 并发能力提升16倍(16个商品可并行扣减)
- 避免单点竞争,实测QPS从300→4500
反思:分段数需压测确定——过少仍竞争,过多增加运维复杂度”
分段锁会遇 ABA 问题吗?如何优化?
“关键澄清:Redis分布式锁本身不涉及ABA(锁操作是原子SET/DEL),但业务逻辑(如库存值)可能遇ABA。
✅ 项目防护:
- 锁仅控制‘扣减操作串行化’,库存值校验交由数据库乐观锁(version字段)
UPDATE product SET stock=stock-1, version=version+1 WHERE id=? AND version=? AND stock>0- Redis锁+DB乐观锁双保险,彻底规避ABA
⚠️ 若纯Redis方案:需在value存版本号,用Lua脚本比对(但增加复杂度,不推荐)
认知:分布式场景下,‘锁’解决并发控制,‘版本号’解决数据一致性,二者分工明确。”
(追问)并发涨10倍,锁设计瓶颈?改进思路?
“瓶颈分析:
- 分段锁上限=分段数×单段QPS(16×300=4800),10倍并发(5万)必超载
改进三阶:
1️⃣ 短期:动态扩容分段数(从16→64),需重启服务(有损)
2️⃣ 中期:
- 引入本地缓存+异步刷盘:
• 本地用ConcurrentHashMap缓存库存(分段锁保护)
• 定时/定量异步同步DB(减少Redis压力)- 限流熔断:Sentinel控制入口流量
3️⃣ 长期:- 改用Redisson分布式锁:
✅ 看门狗自动续期(防业务超时锁失效)
✅ 多种锁模式(公平锁、读写锁)
✅ 与Spring Boot无缝集成- 取舍对比:
方案 优点 缺点 自研分段锁 轻量、可控 需自维护续期/分段逻辑 Redisson 功能全、稳定 引入依赖、学习成本 结论:业务初期自研可控,规模扩大后拥抱成熟框架,把精力聚焦业务创新。”
Redis decr 命令使用场景与注意点?
“典型场景:
- 库存扣减(配合Lua脚本防超卖)
- 接口限流(令牌桶:
decr token_bucket)- 倒计时(活动剩余次数)
⚠️ 关键注意:
1️⃣ 初始化:必须先SET key 100,否则decr返回(nil)(易踩坑!)
2️⃣ 负数风险:decr可使值<0 → 用Lua脚本校验:if redis.call('get', KEYS[1]) > 0 then return redis.call('decr', KEYS[1]) end return -13️⃣ 原子性:单命令原子,但‘判断+扣减’需Lua保证
项目实践:秒杀库存用decr+Lua,压测验证10万次扣减无负数。”
LRU 缓存实现思路?
“数据结构组合拳:
- 哈希表(HashMap):O(1)查找节点
- 双向链表(Linked List):维护访问顺序(头=最新,尾=最旧)
核心操作:
1️⃣ 访问/更新:
- 哈希表查节点 → 移至链表头部
2️⃣ 插入:- 新节点放头部
- 若超容量:删尾部节点 + 哈希表移除
3️⃣ 淘汰:直接删尾部
时间复杂度:所有操作O(1)
Java实现参考:- 标准库
LinkedHashMap(重写removeEldestEntry)- 手写:定义
Node含key/value/prev/next,维护head/tail指针
延伸思考:- 线程安全:高并发场景用
ConcurrentHashMap+ReentrantLock分段锁- 近似LRU:Redis用随机采样(性能与精度权衡)”
你有什么想了解我们团队、业务方向或者实习安排的吗?
#JAVA##面经##Java面经#“感谢提问!我有三个具体问题:
1️⃣ 技术成长:团队对实习生是否有‘导师制’?比如每周代码Review、技术分享参与机会?
2️⃣ 业务价值:当前团队最希望实习生补位的技术环节是什么?(如参与核心模块开发/性能优化)
3️⃣ 长期发展:贵司在[提及公司具体技术方向,如:高并发中间件/云原生]有哪些技术规划?实习生能否接触相关项目?
补充:我渴望的不仅是完成任务,更是理解业务逻辑、参与技术决策,在实战中成长为能独当一面的工程师。”
本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏