2026 Java后端面试“三剑客”:集合、JUC、Redis 高频考点解析
🔥 2026 Java后端面试热点题库:集合 + JUC + Redis 高频基础中档题精讲
本文精选2026年Java后端面试中最热门、最高频的基础到中档难度题目,涵盖Java集合框架、JUC并发编程、Redis缓存三大核心模块。建议收藏反复研读!
一、Java 集合框架(Collection Framework)
1. HashMap 底层原理与扩容机制 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:基础
核心要点
// HashMap 核心结构(JDK 1.8)transientNode<K,V>[]table;// 哈希桶数组transientintsize;// 元素个数intthreshold;// 扩容阈值 = capacity * loadFactorfinalfloatloadFactor;// 负载因子,默认0.75JDK 1.8 的优化改进:
- 数组+链表+红黑树:当链表长度≥8且数组长度≥64时,链表转为红黑树,查询复杂度从O(n)降至O(log n)
- 尾插法:扩容时采用尾插法,避免了JDK 1.7头插法导致的死循环问题
- 扩容优化:扩容时不再重新计算hash,而是通过
(e.hash & oldCap) == 0判断位置
高频追问
Q:为什么HashMap的容量必须是2的幂次方?
答案:为了使用位运算替代取模运算,提升性能。当容量为2^n时,hash % capacity等价于hash & (capacity - 1),位运算效率远高于取模。
Q:HashMap在JDK 1.7和1.8中的主要区别?
| 特性 | JDK 1.7 | JDK 1.8 |
|---|---|---|
| 数据结构 | 数组+链表 | 数组+链表+红黑树 |
| 插入方式 | 头插法 | 尾插法 |
| 扩容 | 重新计算hash | 优化,只需判断高位 |
| 线程安全 | 不安全(死循环风险) | 不安全(数据覆盖) |
2. ConcurrentHashMap 线程安全实现 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:中档
JDK 1.7 vs JDK 1.8 实现对比
// JDK 1.7:分段锁(Segment)finalSegment<K,V>[]segments;// 默认16个Segment,最多16个线程并发写// JDK 1.8:CAS + synchronizedvolatileNode<K,V>[]table;// 锁粒度细化到桶级别JDK 1.8 的核心优化:
- 取消Segment:锁粒度从段级降到桶级,并发度大幅提升
- CAS + synchronized:使用CAS进行无锁插入,冲突时用synchronized锁定头节点
- size计算优化:使用CounterCell数组分散计数,减少竞争
put() 流程详解
// JDK 1.8 put 流程简化版finalVputVal(Kkey,Vvalue,booleanonlyIfAbsent){// 1. 计算hashinthash=spread(key.hashCode());intbinCount=0;for(Node<K,V>[]tab=table;;){Node<K,V>f;intn,i,fh;// 2. 数组未初始化,先初始化if(tab==null||(n=tab.length)==0)tab=initTable();// 3. 目标桶为空,CAS插入(无锁)elseif((f=tabAt(tab,i=(n-1)&hash))==null){if(casTabAt(tab,i,null,newNode<K,V>(hash,key,value,null)))break;}// 4. 正在扩容,协助扩容elseif((fh=f.hash)==MOVED)tab=helpTransfer(tab,f);// 5. 桶非空,synchronized锁定头节点else{synchronized(f){// 链表/红黑树插入逻辑...}}}addCount(1L,binCount);returnnull;}3. ArrayList vs LinkedList ⭐⭐⭐⭐
| 对比维度 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | Object数组 | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 尾部插入 | O(1),扩容时O(n) | O(1) |
| 中间插入 | O(n) | O(1)(查找O(n)) |
| 内存占用 | 较少 | 较多(需存储指针) |
| 适用场景 | 查询多、随机访问 | 频繁插入删除 |
二、JUC 并发编程(java.util.concurrent)
1. AQS(AbstractQueuedSynchronizer)原理 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:中档
AQS是JUC包的基石框架,ReentrantLock、CountDownLatch、Semaphore等都基于它实现。
核心架构
publicabstractclassAbstractQueuedSynchronizerextendsAbstractOwnableSynchronizer{// 核心状态(volatile保证可见性)privatevolatileintstate;// CLH队列(双向链表)privatetransientvolatileNodehead;privatetransientvolatileNodetail;}核心思想:
- state:同步状态,volatile保证线程可见性
- FIFO队列:CLH变体队列管理等待线程
- CAS操作:保证状态更新的原子性
独占式获取锁流程
// ReentrantLock.lock() 最终调用publicfinalvoidacquire(intarg){// 1. 尝试获取锁(子类实现)if(!tryAcquire(arg)&&// 2. 获取失败,加入队列acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}高频追问
Q:AQS为什么使用双向链表而不是单向链表?
答案:
- 支持取消节点:需要修改前驱节点的next指针
- 支持从尾部快速插入:保证入队操作的原子性
- 支持唤醒后继节点:需要快速找到下一个等待线程
2. 线程池核心参数与执行流程 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:中档
七大核心参数
publicThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 空闲线程存活时间TimeUnitunit,// 时间单位BlockingQueue<Runnable>workQueue,// 任务队列ThreadFactorythreadFactory,// 线程工厂RejectedExecutionHandlerhandler// 拒绝策略)任务提交流程
提交任务 ↓ 线程数 < corePoolSize ? ↓ 是 ↓ 否 创建核心线程 队列是否已满? ↓ ↓ 是 ↓ 否 执行任务 线程数 < maxPoolSize ? 加入队列 ↓ 是 ↓ 否 创建非核心线程 执行拒绝策略 ↓ 执行任务四种拒绝策略
| 策略 | 说明 |
|---|---|
| AbortPolicy | 直接抛出RejectedExecutionException(默认) |
| CallerRunsPolicy | 由调用线程执行任务 |
| DiscardPolicy | 静默丢弃任务 |
| DiscardOldestPolicy | 丢弃队列最老的任务,重试提交 |
高频追问
Q:线程池中的Worker为什么要继承AQS?
答案:Worker继承AQS是为了控制线程中断:
lock():线程开始执行任务时加锁(state=1)unlock():任务执行完毕解锁(state=0)shutdown():只中断空闲线程(state=0的线程)shutdownNow():中断所有线程
3. synchronized vs ReentrantLock ⭐⭐⭐⭐⭐
| 对比维度 | synchronized | ReentrantLock |
|---|---|---|
| 实现方式 | JVM层面(monitor) | API层面(AQS) |
| 锁的获取 | 自动获取/释放 | 手动lock/unlock |
| 灵活性 | 低 | 高(可中断、超时、公平锁) |
| 条件变量 | 一个(wait/notify) | 多个(Condition) |
| 性能 | JDK 1.6后优化,接近 | 略优(某些场景) |
| 可重入性 | 支持 | 支持 |
synchronized 锁升级过程:
无锁 → 偏向锁 → 轻量级锁 → 重量级锁三、Redis 缓存
Redis 五种基本数据类型及使用场景
String:验证码、计数器、分布式锁。
Hash:存储对象信息(用户信息)。
List:消息队列、关注列表。
Set:去重、抽奖、共同好友。
ZSet:排行榜(核心是 跳表 SkipList)。
持久化机制:RDB vs AOF
RDB(快照):全量备份,恢复快,但可能丢失最后一次备份后的数据。
AOF(日志):记录每一条写指令,数据安全性高,但文件大、恢复慢。
最佳实践:混合持久化(RDB+AOF)。
缓存三大问题:穿透、击穿、雪崩 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:基础
| 问题 | 定义 | 原因 | 解决方案 |
|---|---|---|---|
| 缓存穿透 | 查询不存在的数据,直接打到DB | 恶意攻击、数据不存在 | ① 布隆过滤器 ② 缓存空值 |
| 缓存击穿 | 热点key过期,大量请求打到DB | 热点key过期瞬间 | ① 互斥锁 ② 逻辑过期 |
| 缓存雪崩 | 大量key同时过期,DB压力激增 | 集中过期、Redis宕机 | ① 随机过期时间 ② 多级缓存 ③ 熔断限流 |
解决方案详解
// 1. 缓存穿透 - 布隆过滤器@ComponentpublicclassBloomFilterHelper{@AutowiredprivateRedissonClientredisson;publicbooleanmightContain(Stringkey){RBloomFilter<String>bloomFilter=redisson.getBloomFilter("userFilter");returnbloomFilter.contains(key);}}// 2. 缓存击穿 - 互斥锁publicStringgetWithLock(Stringkey){Stringvalue=redis.get(key);if(value==null){// 获取分布式锁if(redissonLock.tryLock(key,10,TimeUnit.SECONDS)){try{// 双重检查value=redis.get(key);if(value==null){value=db.query(key);redis.setex(key,3600,value);}}finally{redissonLock.unlock(key);}}}returnvalue;}// 3. 缓存雪崩 - 随机过期时间publicvoidsetWithRandomExpire(Stringkey,Stringvalue){intexpire=3600+newRandom().nextInt(300);// 3600~3900秒redis.setex(key,expire,value);}Redis 分布式锁实现 ⭐⭐⭐⭐⭐
面试频率:极高 |难度:中档
演进过程
# 阶段1:SETNX + EXPIRE(非原子操作,可能死锁)SETNX lock:order:123trueEXPIRE lock:order:12330# 阶段2:SET NX EX(原子操作)SET lock:order:123 random_value NX EX30# 阶段3:Redisson(生产环境推荐)Redisson 分布式锁原理
// Redisson 使用示例RLocklock=redisson.getLock("myLock");try{// 尝试加锁,最多等待10秒,锁30秒后自动释放booleanisLocked=lock.tryLock(10,30,TimeUnit.SECONDS);if(isLocked){// 执行业务逻辑}}finally{lock.unlock();}Watch Dog 自动续期机制:
- 获取锁成功后,启动看门狗线程
- 看门狗每隔10秒(默认)检查锁是否仍被持有
- 如果仍持有,自动续期到30秒
- 解决业务执行时间超过锁过期时间的问题
Redis 为什么这么快?⭐⭐⭐⭐
答案要点:
- 纯内存操作:所有数据在内存中,读写速度极快
- 单线程模型:避免多线程上下文切换和竞争开销
- IO多路复用:基于epoll/kqueue处理高并发连接
- 高效数据结构:SDS、跳表、压缩列表等优化
- RESP协议:简单高效的序列化协议
注意:Redis 6.0 引入多线程,但仅限于网络IO,命令执行仍是单线程。
四、面试技巧总结
回答结构化模板
1. 先给出核心结论(是什么) 2. 详细解释原理(为什么) 3. 对比/演进过程(深度) 4. 实际应用场景(广度) 5. 可能的问题与优化(亮点)高频考点速查表
| 模块 | 必会题目 |
|---|---|
| 集合 | HashMap扩容、ConcurrentHashMap锁机制、红黑树转换条件 |
| JUC | AQS原理、线程池参数、volatile/CAS/synchronized原理 |
| Redis | 缓存三问题、分布式锁、持久化RDB/AOF、主从复制 |
结语
本文涵盖了2026年Java后端面试中最热门的基础到中档难度题目。建议读者:
- 理解原理:不要死记硬背,要理解底层机制
- 动手实践:自己写代码验证,加深理解
- 举一反三:从一个知识点延伸到相关知识点
祝你面试顺利,拿到心仪的Offer!🎉
📌版权声明:本文为原创内容,转载请注明出处。
📅更新时间:2026年4月
💬交流讨论:欢迎在评论区交流面试经验
