网易Java后端开发工程师面试题精选:10道高频考题+答案解析

网易旗下有网易游戏、网易云音乐、网易有道、网易严选等业务线,Java后端开发一直是核心岗位。本文基于真实面试经验整理,覆盖JVM、并发、Spring、Redis、Kafka、MySQL、分布式系统设计等高频考点,每道题都配有通俗易懂的解析和代码示例。


1. ConcurrentHashMap 1.7和1.8有什么区别?为什么1.8要改成CAS+synchronized?

这道题在网易面试中几乎必问,属于并发编程的经典题。

答案:

1.7版本用的是Segment分段锁,默认16个Segment,每个Segment继承ReentrantLock,put操作时对单个Segment加锁,相当于把大锁拆成16把小锁,并发度就是16。

1.8版本完全重写了,改成数组+链表+红黑树的结构,用CAS+synchronized来实现并发控制。put操作时先CAS尝试插入头节点,如果头节点为空直接CAS成功,否则对头节点加synchronized锁。

为什么这么改?核心原因有三个:

  • 锁粒度更细:1.7要锁一个Segment(包含多个桶),1.8只锁一个桶,并发度从16提升到数组长度

  • synchronized优化了:JDK 1.6之后synchronized引入偏向锁、轻量级锁,性能已经不输ReentrantLock了

  • 节省内存:Segment继承ReentrantLock是有额外内存开销的

复制代码
 
java 复制代码
// 1.8 put操作的简化逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 计算hash
    int hash = spread(key.hashCode());
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable(); // CAS初始化
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // 头节点为空,CAS插入,无锁操作!
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break;
        } else {
            // 对头节点加synchronized锁
            synchronized (f) {
                // ... 链表或红黑树插入
            }
        }
    }
}

2. JVM内存模型和垃圾回收机制,网易云音乐场景下怎么调优?

这道题考察你对JVM的理解深度,面试官会追问实际调优经验。

答案:

JVM内存分线程私有和线程共享两块。线程私有的有程序计数器、虚拟机栈(每个方法对应一个栈帧)、本地方法栈;线程共享的有堆(存对象实例)和方法区(存类信息、常量、静态变量)。

垃圾回收最常用的是分代收集:新生代用复制算法(Minor GC频繁,存活对象少,复制成本低),老年代用标记-整理或标记-清除(CMS/G1)。

网易云音乐这种场景,数据特征是一条歌曲有上亿次播放,用户听歌记录、歌单、评论都是热点数据。常见调优:

复制代码
 
java 复制代码
// 1.8 put操作的简化逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 计算hash
    int hash = spread(key.hashCode());
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable(); // CAS初始化
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // 头节点为空,CAS插入,无锁操作!
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break;
        } else {
            // 对头节点加synchronized锁
            synchronized (f) {
                // ... 链表或红黑树插入
            }
        }
    }
}

关键思路是:评论、歌单这类热点数据会频繁进入老年代,用G1可以设置目标停顿时间,避免Full GC导致接口超时。如果接口超时频繁,可以通过jstat -gcutil观察GC频率,适当调大新生代或者调整IHOP( Initiating Heap Occupancy Percent)。


3. 说一下Spring的IOC和AOP,AOP在项目里怎么用的?

Spring几乎是网易后端项目的标配,这道题看你有没有真正用过还是只会背八股。

答案:

IOC(控制反转)就是把对象创建和依赖管理的控制权从程序员手里交给Spring容器。原来我们new对象,现在把对象交给容器管理,通过构造器或setter注入依赖。Spring Boot里加个@Service或@Component,容器启动时就会扫描并创建Bean。

AOP(面向切面编程)底层分两种:有接口就用JDK动态代理(基于反射+InvocationHandler),没有接口就用CGLIB(生成子类)。

项目中真实场景:

复制代码
 
java 复制代码
@Aspect
@Component
public class LogAspect {
    
    // 环绕通知,记录接口调用耗时
    @Around("@annotation(LogAnnotation)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = point.proceed();
        long cost = System.currentTimeMillis() - start;
        // 写入日志或监控系统
        log.info("{} 耗时: {}ms", point.getSignature(), cost);
        return result;
    }
}

网易云音乐项目里,AOP常用于:记录用户操作日志(收藏/评论/分享)、接口耗时监控、权限校验、分布式锁等。判断一个人有没有真用过AOP,就看他说不谈什么"日志/事务/权限",而是能说清在这些场景里遇到了什么问题、怎么处理的。


4. Redis如何实现排行榜?网易云音乐歌单热度排序

网易云音乐的排行榜功能每天上亿用户访问,Redis的Sorted Set就是干这个的。

答案:

用Sorted Set(有序集合),每个成员关联一个score,Redis自动按score排序。

复制代码
 
java 复制代码
// 网易云音乐热歌榜实现思路

// 1. 每首歌曲的播放次数作为score
String key = "rank:hot_songs";
// 播放一次,score加1
redisTemplate.opsForZSet().incrementScore(key, "song_10086", 1);

// 2. 获取Top 100
Set<Object> top100 = redisTemplate.opsForZSet()
    .reverseRange(key, 0, 99); // 从高到低取100个

// 3. 获取某首歌的排名
Long rank = redisTemplate.opsForZSet()
    .reverseRank(key, "song_10086"); // 排名从0开始

真实场景的难点:

  • 冷热数据分开:周榜和总榜分不同的key,周榜每周清空,总榜定期归档到MySQL/ES

  • 降权处理:新歌需要扶持,比如新发布的歌7天内score乘以1.2的权重,防止老歌霸榜

  • 防刷:一个用户短时间内同一首歌播放多次需要限频,一般加上时间窗口过滤(比如30秒内重复播放不计数)

  • 实时与最终一致:排行榜可以容忍几秒的延迟,所以用Redis做主存储,后台定时刷到数据库做持久化


5. MySQL慢查询怎么优化?一条SQL从1秒优化到10ms

面试官会给你一条具体SQL让你分析为什么慢,怎么优化。

答案:

第一步,找到慢SQL。 MySQL的慢查询日志打开:

复制代码
 
java 复制代码
-- 开启慢查询日志,设置阈值1秒
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;

第二步,用EXPLAIN分析。 重点关注几个字段:type(至少要range以上,最好是ref或const)、Extra(如果有Using filesort或Using temporary就要优化了)。

第三步,常见的优化手段:

  • 索引优化:联合索引遵循最左前缀原则。比如要查where status=1 and create_time>'2025-01-01',建(status, create_time)的联合索引,别单建

  • 覆盖索引:查询的字段都在索引里,不用回表

  • 分页优化:深分页用子查询或游标,别直接limit 1000000, 20

复制代码
 
java 复制代码
-- 深分页优化:用上一次的ID代替limit偏移
-- 慢写法
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

-- 快写法
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;
  • 读写分离:读多写少的场景,主库写、从库读

  • 分库分表:单表超过500万行就得考虑分表,按用户ID取模或者按时间分区


6. 订单超时自动取消怎么实现?网易严选场景

网易严选是电商业务,订单30分钟未支付自动取消是经典问题。

答案:

方案一:定时任务扫描(最原始,不推荐)

缺点很明显,扫全表、延迟高、浪费数据库IO。

方案二:Redis过期事件

复制代码
 
java 复制代码
// 订单创建后设置缓存,过期时间30分钟
redisTemplate.opsForValue().set(
    "order:timeout:" + orderId, 
    orderId, 
    30, 
    TimeUnit.MINUTES
);

但Redis过期事件不保证100%触发(key被删除也可能不回调),而且延迟不确定,只能当辅助方案。

方案三:时间轮算法 + 延迟消息

用Kafka或RocketMQ的延迟队列。RocketMQ支持18个级别的延迟(1s/5s/10s/30s/1m/2m...30m),发消息时指定延迟级别,到期自动投递。

复制代码
 
java 复制代码
// RocketMQ 延迟消息
Message msg = new Message("order_topic", orderId.getBytes());
msg.setDelayTimeLevel(16); // 30分钟
producer.send(msg);

方案四:Redisson DelayedQueue

如果不想引入MQ,用Redisson的RDelayedQueue,底层依赖Redis的Sorted Set,到期自动取出。

网易这种体量的公司,一般是RocketMQ延迟队列 + Redis过期兜底的双重方案,消息队列负责准时触发,Redis过期事件做异常情况的补偿。如果MQ挂了,还有定时任务做最终的兜底扫描。


7. 分布式锁有几种实现方式?网易游戏道具发放怎么保证不超发?

网易游戏在抢道具、充值、活动奖励时,分布式锁是关键。

答案:

实现方式有三种:

方案一:Redis SETNX

复制代码
 
java 复制代码
// 获取锁,key=道具ID,value=请求唯一ID,过期时间5秒
Boolean locked = redisTemplate.opsForValue()
    .setIfAbsent("lock:item:10001", requestId, 5, TimeUnit.SECONDS);
if (locked) {
    try {
        // 发放道具的逻辑
    } finally {
        // 释放锁,用Lua脚本保证原子性
        String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class), 
            Arrays.asList("lock:item:10001"), requestId);
    }
}

方案二:Redisson

Redisson的RLock实现了可重入、自动续期(看门狗机制)、公平锁等高级功能。

方案三:Zookeeper

基于临时顺序节点+Watch机制,可靠性比Redis高,但性能差一点,适合对一致性要求极高的场景(比如道具发放)。

难点在于:

  • 锁的超时时间怎么设?太短业务没执行完锁就自动释放了,太长影响并发

  • Redisson有看门狗,默认每10秒续期一次,业务不结束就不释放锁

  • Redis主从架构下,如果获取锁后主节点挂了,从节点还没同步,其他线程又能获取到锁,这就是RedLock要解决的问题


8. Kafka怎么保证消息不丢失和不重复消费?

网易有道、云音乐这些业务都有大量异步消息场景,消息可靠性是必考题。

答案:

消息不丢失,分三个阶段来看:

  • 生产者端:用同步发送+回调确认
复制代码
 
java 复制代码
// 同步发送,确认leader和follower都写入
producer.send(record).get();  // 同步等待
// 或者用回调
producer.send(record, (metadata, exception) -> {
    if (exception != null) {
        // 重试或记录失败消息
    }
});

配置acks=all(或acks=-1),等所有副本都写入了再返回成功。

  • Broker端:复制因子replication.factor >= 3,min.insync.replicas=2,保证至少2个副本同步。

  • 消费者端:关闭自动提交,业务处理完才手动提交offset

复制代码
 
java 复制代码
// 手动提交offset
consumer.commitSync();  // 在业务处理成功后调用

消息不重复消费,核心是实现幂等性:

  • 生产端幂等:enable.idempotence=true,Kafka会去重

  • 消费端幂等:每条消息带全局唯一ID(或业务唯一键),消费前先查是否处理过

复制代码
 
java 复制代码
// 消费端幂等处理
@Override
public void onMessage(Message message) {
    String bizId = message.getBizId(); // 业务唯一ID
    // Redis SETNX,如果已经处理过就跳过
    Boolean done = redisTemplate.opsForValue()
        .setIfAbsent("msg:processed:" + bizId, "1", 1, TimeUnit.DAYS);
    if (!Boolean.TRUE.equals(done)) {
        return; // 已消费,跳过
    }
    // 真正的业务处理...
}

9. Elasticsearch的写入和查询流程,网易有道搜索怎么做?

网易有道的词典搜索、题库搜索大量使用ES。

答案:

写入流程: 客户端请求发给任意一个协调节点 → 协调节点计算该文档应该到哪个分片(shard = hash(_id) % number_of_shards) → 转发到主分片节点 → 写入buffer同时写translog → 每秒refresh生成一个segment(此时才可搜索) → 定期flush把segment刷到磁盘。

查询流程: 客户端发查询到协调节点 → 协调节点转发到所有分片(从分片和主分片都可以查) → 每个分片本地查询返回结果 → 协调节点合并排序返回。

面试的经典追问:

Q:ES的near real-time(近实时)是什么意思?

A:写入后默认1秒才可搜索,因为refresh默认1秒执行一次。如果要求强实时,可以调refresh=wait_for或手动refresh,但写入性能会下降。

Q:ES的深度分页怎么优化?

A:from+size不能超过10000条,超过要用search_after或scroll。search_after基于上一页最后一个文档的排序值继续分页,性能好且稳定。

复制代码
 
java 复制代码
// search_after分页
GET /products/_search
{
  "size": 20,
  "sort": [{"price": "asc"}, {"id": "asc"}],
  "search_after": [99, "product_12345"]  // 上一页最后一个商品的price和id
}

10. 设计一个带热度排序的Feed流系统(网易云音乐动态/歌单推荐场景)

这是系统设计题,网易很喜欢考这种结合业务场景的设计。

答案:

需求分析: 用户关注了歌手/好友后,能看到他们的最新动态(发歌、评论、收藏等),按热度排序。

推拉模式选择:

  • 推模式:用户发动态后,写入所有粉丝的收件箱。适合明星大V(粉丝多时写入压力大)

  • 拉模式:动态只存一份,用户刷Feed时拉取所有关注的人的动态再合并排序。适合粉丝少的普通用户(关注多时拉取压力大)

  • 混合模式:大V用推(活跃粉丝直接推送),普通用户用拉。这是网易云音乐实际采用的方式

架构设计:

复制代码
 
java 复制代码
用户发动态 → API网关 → MQ(削峰) → Feed服务
  ├─ 大V发的 → 写入粉丝的Redis收件箱(Sorted Set,score=时间戳)
  ├─ 普通用户 → 只写入自己的时间线
  └─ 同步到MySQL持久化

用户刷Feed → Feed服务
  ├─ 从Redis收件箱取(已push的)
  ├─ 从关注列表拉普通用户的动态
  ├─ 合并排序(按时间/热度加权)
  └─ 返回给用户

热度排序实现:

复制代码
 
java 复制代码
// 热度的简单公式
double hotScore = baseScore * (1 + weight * factor);
// - baseScore:基础分(比如赞数*1 + 评论数*2 + 转发数*3)
// - weight:时间衰减权重,越新越高
// - factor:内容质量系数(通过用户举报率等调整)

// 用Redis Sorted Set存储,score就是热度值
String key = "feed:timeline:" + userId;
redisTemplate.opsForZSet()
    .add(key, feedItemId, hotScore);
// 取出Top 50
redisTemplate.opsForZSet()
    .reverseRange(key, 0, 49);

亿级用户的优化:

  • 收件箱不存完整动态,只存动态ID,详情从Redis缓存或MySQL按ID批量查询

  • 冷用户不预推送,等他登陆时实时聚合

  • 缓存穿透问题:热点数据缓存+布隆过滤器拦截不存在的数据


总结

网易Java后端面试有个特点:既考基本功,又考场景设计。八股文答得好只是及格,能结合业务场景把知识"用起来"才是加分项。建议准备面试的时候,每个知识点都想一想"如果在网易云音乐/网易游戏/网易有道,这个技术会用在什么场景"。

祝大家面试顺利,早日上岸!

相关推荐
nashane2 小时前
HarmonyOS 6学习:应用推广引擎评论管理与长截图自动拼接实战
学习·华为·harmonyos·harmonyos 5
良木生香2 小时前
【C++初阶】STL——Vector从入门到应用完全指南(1)
开发语言·c++·神经网络·算法·计算机视觉·自然语言处理·数据挖掘
key_3_feng2 小时前
鸿蒙基于润和DAYU200(RK3568)开发板的系统移植与实战开发
华为·harmonyos
Swift社区2 小时前
Store + System:鸿蒙游戏黄金分层
游戏·华为·harmonyos
babe小鑫2 小时前
数据岗位的发展与学习数据分析指南
学习·数据挖掘·数据分析
_waylau2 小时前
历时三年《鸿蒙系统(HarmonyOS)移动开发实战》简介
华为·harmonyos·鸿蒙·鸿蒙系统
想你依然心痛2 小时前
HarmonyOS 6(API 23)实战:打造“空间相册“——基于 Face AR 表情驱动 + 沉浸光感悬浮导航的 PC 端沉浸式照片浏览系统
华为·ar·harmonyos·悬浮导航·沉浸光感
maaath2 小时前
【maaath】 Flutter for OpenHarmony 快捷工具箱应用实战开发
flutter·华为·harmonyos
AI科技星2 小时前
全域数学视角下N维广义数系的推广与本源恒等式构建【乖乖数学】
人工智能·机器学习·数学建模·数据挖掘