发布时间:2026/8/31 4:17:46
Java后端面试高频考点:集合、并发、Spring、MySQL与Redis全解析 Java 后端面试准备最怕的不是不知道要考什么而是知道要考什么却只背下了答案讲不出原理。真正的高频面试题很少是孤立的一个问题比如问你 HashMap下一步就会问扩容、哈希冲突、为什么用红黑树、线程安全问题再往下就会引到 ConcurrentHashMap、JMM、volatile。面试官考察的不是某个 API 有没有见过而是这些知识在你脑中是否连成一张网。下面围绕 Java 后端面试高频考点按 Java 核心、Spring、MySQL、Redis、分布式和现场表达六个模块梳理一条可执行的复习主线并给出代码、配置、排查命令和答题建议。复习时间有限时按这条线走比漫无目的刷几百道题更有价值。1. 面试准备的核心策略把高频知识点串成网络1.1 Java 后端面试通常考察五类能力从招聘 JD 和真实面试反馈来看Java 后端面试在知识维度上基本可以分成五类Java 语言基础与并发、Spring 生态与原理、数据存储与 SQL 优化、分布式中间件、项目表达与工程能力。很多候选人会把时间平均花在所有知识点上结果每一块都只记得概念。更好的做法是先把这五类能力里最高频的知识点列出来逐个确认自己能不能回答“为什么”。所谓高频不一定是网上流传的固定题单而是无论面试官怎么换角度提问最终都会落到的核心机制。考察维度高频主题建议准备深度Java 核心HashMap、并发工具、JMM、JVM 内存与 GC能画出结构能写出关键代码能说出失败场景Spring 生态Bean 生命周期、循环依赖、事务、自动配置能描述完整流程能举例说明什么情况会失效数据存储索引、B Tree、MVCC、隔离级别、慢 SQL能通过 EXPLAIN 定位问题能对比方案取舍分布式中间件Redis 缓存、分布式锁、消息队列、幂等能给出伪代码能说清失败时的处理方式项目与工程项目难点、方案选型、监控、上线回滚能按背景、行动、结果的方式组织表达这里要特别注意面试并不是考试做题更多是“技术对话”。面试官会根据你的一句话往下追问。你说到自己用过 Redis 缓存后面很可能就是缓存穿透、缓存一致性、分布式锁三个方向连着一路问。1.2 知识点散装是面试最大的风险散装的意思是每个概念都听说过但概念之间连不起来。最常见的例子就是 volatile。问你 volatile 能保证什么你会答可见性和有序性。问你它能不能保证原子性你也能说不能。但如果问你为什么 DCL 单例需要 volatile你可能会卡住。再追问 volatile 和 synchronized 在 JMM 里分别解决了什么问题你可能会开始犹豫。这说明知识是片段式的。面试官不会直接按填空题出题而是按业务场景和因果链提问。准备时最好把同一个概念相关的“问题链”一起复习。一条常见的问题链是HashMap 底层结构 - 哈希冲突怎么解决 - 扩容机制是什么 - 线程安全吗 - 为什么不安全 - ConcurrentHashMap 怎么保证安全 - synchronized 和 CAS 在这里怎么用 - 这和 JMM 有什么关系建议复习时不要只看单一题目的答案而是主动给自己多问一步“为什么”。如果发现自己只能答出结论说不出推导过程那就说明这个知识点还没有真正掌握。2. Java 核心高频题集合、并发与 JVM 在问同一件事2.1 HashMap 的哈希冲突、树化与扩容逻辑HashMap 是 Java 面试中出现频率最高的集合类之一。它解决的问题很简单用哈希表结构让查询、插入的平均时间复杂度接近 O(1)。在 JDK 8 及以后的版本中HashMap 的底层是数组加链表当链表长度达到阈值时会转换成红黑树。put 数据时先通过 key 的哈希值计算数组下标遇到哈希冲突就挂到同一个桶的链表上。// 参考 JDK 8 的哈希扰动逻辑 static final int hash(Object key) { int h; return (key null) ? 0 : (h key.hashCode()) ^ (h 16); } // 数组下标计算 int index (table.length - 1) hash(key);这段代码有两个关键设计。第一将 hashCode 的高 16 位和低 16 位做异或目的是让高位也参与下标计算减少哈希冲突。第二使用(length - 1) hash而不是取模是因为数组长度总是 2 的幂次方位运算比取模更快。树化条件不是“链表长度大于等于 8”这么简单完整条件是链表长度达到 8 且数组长度达到 64。如果数组容量还没到 64就算链表很长也会先扩容而不是树化。红黑树的引入是为了让极端冲突情况下的查询从 O(n) 降到 O(log n)。负载因子 0.75 是时间成本和空间成本的折中默认值不是随意定的。常见追问点是扩容。扩容时数组长度变为原来的两倍已经存在的元素需要重新计算下标。这里要回答出扩容带来的性能损耗以及为什么 HashMap 不适合高并发场景多线程同时 put 可能导致数据覆盖JDK 7 甚至可能因为头插法产生循环链表JDK 8 改成了尾插法但仍然线程不安全。2.2 ConcurrentHashMap 的并发控制策略面试官问你“HashMap 线程不安全那用什么”重点不是让你报出 ConcurrentHashMap 这个类名而是看你知不知道它为什么安全。JDK 8 的 ConcurrentHashMap 放弃了 JDK 7 的分段锁设计改为数组加链表加红黑树的结构。并发控制上做了两个关键处理桶为空时使用 CAS 操作把节点放入数组避免加锁。桶不为空时使用 synchronized 锁住桶中的首节点锁粒度从一个分段缩小到一个桶。相比 JDK 7 按 Segment 分段加锁JDK 8 的并发度更高因为不同桶之间不会互相影响。size() 统计时也不是直接加锁遍历所有数据而是通过 baseCount 和 CounterCell 数组合并计算降低竞争。// 伪代码表示 put 时的并发控制思路 if (tabAt(tab, i) null) { if (casTabAt(tab, i, null, new NodeK,V(hash, key, value))) { // 插入成功结束 } } else { synchronized (node) { // 锁住桶首节点后处理链表或红黑树 } }面试中如果能说出“CAS 配合 synchronized把锁从容器级别降到了桶级别”这一步已经能超过很多候选人。进一步被问到扩容时可以补充 JDK 8 支持多线程协助扩容这也是 ConcurrentHashMap 为了提升性能做的设计。2.3 volatile、synchronized 与 JMM 的内存语义JMM 是 Java 内存模型的缩写它定义了线程和主内存之间的抽象关系。线程之间的共享变量不会直接在主内存中随时同步每个线程都有自己的工作内存。volatile 和 synchronized 就是为了解决这个模型下的可见性和顺序性问题。volatile 的作用可以概括为两条保证变量在多个线程之间的可见性禁止指令重排序。它不保证原子性。典型的应用是双重检查锁单例public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里 instance 必须用 volatile 修饰。原因在于new Singleton()不是一个原子操作它可以被拆分为分配内存、初始化对象、把引用赋值给变量三步。JVM 可能为了性能对后面的步骤进行重排序导致另一个线程提前拿到一个尚未完成初始化对象的引用。volatile 能禁止这种重排序。synchronized 则是通过监视器锁实现互斥线程进入 synchronized 代码块前会获取锁退出后释放锁同时保证锁内修改的变量在释放锁时写回主内存。面试时不要只背“加锁”要把锁和内存可见性联系起来。2.4 JVM 内存区域、对象分配和垃圾回收JVM 相关问题在 Java 后端面试中基本不会缺席尤其是内存区域、垃圾回收和线上故障排查。JVM 内存区域通常可以分为堆对象实例分配的主要区域也是 GC 的重点区域。虚拟机栈每个线程私有的栈帧保存在这里方法调用过深会抛出 StackOverflowError。方法区 / 元空间存放类元信息、静态变量、常量池等。程序计数器记录当前线程执行的字节码行号。本地方法栈为 native 方法服务。对象分配时绝大多数对象优先在新生代的 Eden 区分配。Eden 区空间不足时触发 Minor GC存活对象进入 Survivor 区经过多次回收仍在的对象进入老年代。大对象会直接进入老年代避免在新生代反复复制。GC 算法上新生代常用复制算法老年代使用标记整理或标记清除类算法。面试被问到“线上 CPU 飙升怎么排查”时可以按这个顺序回答# 1. 先找到占用 CPU 高的 Java 进程 top # 2. 查看进程内的线程 CPU 占用 top -Hp pid # 3. 将线程 ID 转为十六进制 printf %x\n tid # 4. 打印线程栈定位问题代码 jstack pid | grep -A 20 tid-hex如果问题不是 CPU 而是内存可以用 jmap 查看堆内存占用并生成 dump 文件再用 MAT 或其他分析工具定位大对象。命令作用典型场景jps查看 Java 进程确认进程 IDjstack打印线程栈定位死锁、线程阻塞、CPU 高jmap查看堆信息、生成 dump排查堆内存泄漏jstat查看 GC 情况判断 GC 是否频繁jcmd综合排查命令查看 JVM 参数、运行信息OOM 不能只背一个 OutOfMemoryError。要区分堆内存溢出、元空间溢出和栈溢出不同场景的排查重点完全不同。堆 OOM 多数是对象过量或内存泄漏元空间 OOM 常见于动态生成类过多栈溢出则要看递归深度和线程栈大小。2.5 并发工具与线程池的常考点除了集合和 JMMJava 并发部分还有两个重点AQS 和线程池。AQS 是 AbstractQueuedSynchronizer 的缩写很多并发工具如 ReentrantLock、CountDownLatch、Semaphore 都基于它实现。核心思想是维护一个 volatile 的 state 状态变量和一个等待队列。获得锁就是把 state 从 0 改成 1获取失败就进入等待队列。线程池更常被问到。核心参数有 corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory、handler。要能说清楚一个任务提交后当前线程数小于核心线程数直接创建核心线程执行。当前线程数大于等于核心线程数任务放入队列。队列满继续创建非核心线程。线程数达到最大值且队列满触发拒绝策略。拒绝策略有 AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。生产环境最常用 CallerRunsPolicy因为它不会丢弃任务而是把任务交回调用线程执行相当于天然限流。这里有个常见坑不要用 Executors.newFixedThreadPool 的无界队列任务堆积会导致内存暴涨推荐手动 new ThreadPoolExecutor 并设置有界队列和明确拒绝策略。3. Spring 与 Spring Boot 的原理要能讲出为什么3.1 Bean 生命周期和三级缓存解决循环依赖Spring 面试题很少只问“Bean 的生命周期是什么”更多是结合循环依赖、AOP、事务一起问。Bean 生命周期可以概括为解析 BeanDefinition得到类的信息和初始化参数。实例化 Bean通过构造器创建对象。属性填充处理依赖注入。执行 Aware 接口回调。执行 BeanPostProcessor 的前置方法。执行初始化方法比如 InitializingBean 或 init-method。执行 BeanPostProcessor 的后置方法这里会生成 AOP 代理。将最终对象放入单例池。循环依赖的场景是A 依赖 BB 依赖 A。如果只做实例化和属性填充两步A 在填充 B 时发现 B 需要 A但 A 还没创建完就会死循环。Spring 用了三级缓存来解决// 一级缓存存放完整对象 private final MapString, Object singletonObjects new ConcurrentHashMap(256); // 二级缓存存放提前暴露的原始对象 private final MapString, Object earlySingletonObjects new ConcurrentHashMap(16); // 三级缓存存放对象工厂 private final MapString, ObjectFactory? singletonFactories new HashMap(16);实例化 A 后先把 A 的 ObjectFactory 放入三级缓存再继续填充属性。填充 B 时B 反过来需要 ASpring 能从三级缓存中找到 A 的工厂生成一个提前暴露的对象给 B 使用。这里的关键点是如果不涉及 AOP二级缓存足够但 Spring 需要保证循环依赖中拿到的是代理对象所以先用 ObjectFactory 延迟处理代理逻辑。要注意循环依赖不是所有情况都能解决。构造器注入形成的循环依赖无法解决因为实例化阶段还没结束三级缓存里还没有对象。这也是为什么 Spring 官方建议属性字段注入或 setter 注入但实际开发中构造器注入更利于约束不可变性两者需要权衡。3.2 Spring 事务失效的六类常见场景事务失效是 Spring 面试的高频追问点。很多候选人能背出Transactional注解但被问到“为什么一个方法里调另一个方法事务会失效”就答不上来。先理解基础Spring 事务依赖 AOP 代理。外部调用进入代理对象后代理会开启事务再调用目标方法。如果方法内部直接调用同一个类的另一个方法调用的是this目标对象的方法没有经过代理类所以事务注解不会生效。常见失效场景如下失效场景原因检查与处理方法不是 publicSpring 默认只对 public 方法生成事务代理改为 public 或用 TransactionTemplate同类内部自调用调用没有经过代理对象注入自身代理或拆分到另一个 Bean异常被 catch 掉事务感知不到异常不会回滚让异常正常抛出或手动回滚抛出 checked 异常默认只回滚 RuntimeException 和 Error设置 rollbackFor Exception.class多线程执行事务是线程绑定的子线程拿不到主线程事务上下文不在子线程中执行事务逻辑数据库不支持事务比如 MyISAM 引擎确认表引擎是 InnoDB实际项目里最常见的错误是异常被吞掉。比如Transactional public void createOrder(OrderDTO dto) { try { // 业务逻辑 insertOrder(dto); } catch (Exception e) { log.error(create order error, e); // 异常没有继续抛出事务不会回滚 } }这种写法的后果是数据可能只插入了一半但事务已经提交。推荐做法是让业务异常抛出或者在 catch 块中调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。3.3 Spring Boot 自动配置的核心流程Spring Boot 让开发变简单的核心不是“不用配置”而是把配置和默认行为封装在自动配置里。自动配置的入口是SpringBootApplication它组合了EnableAutoConfiguration。EnableAutoConfiguration会通过AutoConfigurationImportSelector加载 classpath 下的自动配置类。Spring Boot 2.7 之前常见的是读取META-INF/spring.factories之后版本推荐使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。真正让自动配置生效的是条件注解。比如ConditionalOnClassclasspath 中有某个类才生效。ConditionalOnMissingBean容器中没有某个 Bean 才生效。ConditionalOnProperty配置项满足条件才生效。配置类示例AutoConfiguration ConditionalOnClass(ExampleService.class) EnableConfigurationProperties(ExampleProperties.class) public class ExampleAutoConfiguration { Bean ConditionalOnMissingBean public ExampleService exampleService() { return new ExampleService(); } }对应的属性配置类ConfigurationProperties(prefix example) public class ExampleProperties { private String name; private int timeout; // getter / setter }面试时要能说清楚自动配置不是把 Bean 无条件注册进去而是先判断条件再决定是否创建默认 Bean。如果你自己在容器中定义了一个同类型 BeanConditionalOnMissingBean会让自动配置的默认 Bean 不生效这也解释了为什么 Spring Boot 既默认又灵活。3.4 Controller、Service、Mapper 分层设计常被追问框架原理之外后端开发的基础分层也是 Java 后端面试的常见内容。一个标准请求处理链路通常是Controller 接收参数 - Service 处理业务逻辑 - Mapper 操作数据库 - 返回 DTO/VO面试官如果让你“写出一个用户查询接口”重点不是看你能不能调通而是看你会不会处理参数校验、异常和返回结构。一个相对完整的 Controller 示例RestController RequestMapping(/api/users) public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService userService; } GetMapping(/{id}) public ResultUserVO getUser(PathVariable Long id) { if (id null || id 0) { throw new IllegalArgumentException(用户 ID 不合法); } return Result.success(userService.getById(id)); } }这里有几个值得提的点使用构造器注入而不是字段注入便于测试参数校验放在接口层业务异常通过统一异常处理返回而不是在 Controller 里写大量 try catch返回值用统一 Result 结构便于前端处理。4. MySQL 索引、事务和 SQL 优化4.1 索引是怎么让查询变快的数据库索引在 Java 后端面试中的地位不亚于 HashMap。回答索引问题时不能只说“索引能加速查询”而要讲清楚 InnoDB 默认使用 B Tree 索引以及为什么选择 B Tree。B Tree 是一种多路平衡查找树特点包括非叶子节点只存索引键不存数据所以单个节点能存放更多索引项树高度更低。叶子节点按顺序存储索引键和数据指针。叶子节点之间通过双向链表连接方便范围查询和排序。每次查找的磁盘 IO 次数相对稳定。相比哈希索引B Tree 支持范围查询。相比普通 B TreeB Tree 把所有数据放在叶子节点借助链表让范围查询更高效。InnoDB 的聚簇索引就是 B Tree叶子节点中直接保存整行数据。二级索引的叶子节点保存主键值查询时需要通过主键索引回表。4.2 回表、覆盖索引和最左前缀原则假设有一张用户表CREATE TABLE user ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT NOT NULL, PRIMARY KEY (id), KEY idx_name_age (name, age) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;执行下面的查询SELECT * FROM user WHERE name 张三;这条 SQL 会先通过联合索引idx_name_age找到 name 对应的主键 id然后再用 id 到聚簇索引里查完整行。这个二次查找过程就是回表。如果想避免回表可以改成SELECT id, name, age FROM user WHERE name 张三;因为要查询的三个字段都在联合索引idx_name_age中叶子节点能直接提供数据不需要回表这叫覆盖索引。最左前缀原则也很常考。联合索引(a, b, c)能命中WHERE a 1WHERE a 1 AND b 2WHERE a 1 AND b 2 AND c 3如果查询条件不包含最左侧的 a例如WHERE b 2 AND c 3索引就不会被有效使用。这不是说其他条件完全不能用索引而是无法从最左列开始定位范围效果会差很多。4.3 事务隔离级别与 MVCC 版本链事务的四大特性 ACID 是基础更常考的是隔离级别。MySQL InnoDB 支持四种隔离级别隔离级别脏读不可重复读幻读读未提交可能可能可能读已提交不可能可能可能可重复读不可能不可能可能串行化不可能不可能不可能MySQL 默认隔离级别是可重复读。它通过 MVCC 多版本并发控制解决了一部分问题。MVCC 使用 undo log 保存数据的历史版本每行数据可能有多个版本事务执行普通读取时通过 ReadView 判断哪些版本对当前事务可见。读已提交和可重复读在这一步有明显区别读已提交每次 SELECT 都会生成一个新的 ReadView所以同一事务中两次查询可能看到不同数据。可重复读只在第一次 SELECT 时生成 ReadView之后复用所以同一事务内普通读看到的是同一份快照。对于幻读MVCC 的普通快照读可以解决大部分场景但当前读比如SELECT ... FOR UPDATE仍然可能产生幻读。InnoDB 通过间隙锁和 next-key lock 在可重复读级别下尽量阻止幻读。4.4 慢 SQL 排查链路与优化方法面试官可能会给一条执行很慢的 SQL让你分析原因。回答时不要直接猜应该按排查链路走确认是否走了索引通过 EXPLAIN 查看执行计划。确认是否发生回表回表次数是否过多。确认是否有隐式类型转换导致索引失效。确认是否对索引列使用了函数或前置模糊匹配。确认数据量和锁等待情况。一条典型的排查 SQLEXPLAIN SELECT id, name FROM user WHERE age 20 ORDER BY create_time LIMIT 1000, 20;EXPLAIN 输出中重点看这几列列名含义关注点type访问类型至少是 range最好达到 ref 或 constkey实际使用的索引NULL 说明没走索引rows预估扫描行数值越大越危险Extra附加信息出现 Using filesort、Using temporary 要优化常见的优化措施包括为查询条件建立联合索引。避免SELECT *只查需要的列。大分页场景改为延迟关联先查主键再 join 回原表。对索引列避免使用函数和隐式转换。复合索引遵循最左前缀原则。这里有一个常见坑WHERE name LIKE %张%是以通配符开头索引会失效而WHERE name LIKE 张%可以走索引。实际开发中如果必须做模糊搜索可以考虑全文索引或搜索引擎而不是无限制依赖数据库 like。5. Redis 高频考点数据结构、缓存一致性和高可用5.1 五种基础数据结构与典型场景Redis 常被用来做缓存但它的价值不只是缓存而是那几种数据结构的灵活运用。数据结构典型场景String缓存对象、计数器、分布式锁、Session 存储Hash对象属性缓存、购物车、字典数据List消息队列、最新列表、发布订阅辅助Set去重、共同好友、抽奖ZSet排行榜、延时队列、带权重的任务调度如果只记住这些场景还不够。面试官可能会继续问 String 的底层是 SDS 简单动态字符串为什么不用 C 字符串因为 SDS 能 O(1) 获取长度、避免缓冲区溢出、二进制安全。ZSet 底层由跳表和哈希表组合实现跳表让范围查询高效哈希表让按 member 查找 score 高效。持久化也要会对比。RDB 是定期生成全量快照恢复快但可能丢失最后一次快照之后的数据AOF 是追加写命令日志数据安全更高但文件更大。Redis 默认开启 RDB如果想兼顾安全可以使用 AOF 或混合持久化。生产环境如何取舍要结合业务允许丢失多少数据来判断。5.2 缓存穿透、击穿、雪崩的区分和应对缓存相关的三道经典题是穿透、击穿、雪崩。很多人背了答案但一被问到“为什么穿透和击穿不一样”就混淆。先把定义分开穿透查询的数据在数据库和缓存中都不存在每次请求都直接打到数据库。击穿某个热点 key 在缓存过期的一瞬间大量请求同时打到数据库。雪崩大量 key 同时失效或者 Redis 整体不可用导致数据库压力暴增。应对方案对比问题原因解决思路穿透查询不存在的数据布隆过滤器拦截缓存空值并设置短过期时间击穿单个热点 key 失效互斥锁重建缓存逻辑过期雪崩大量 key 同时失效过期时间加随机值多级缓存熔断降级布隆过滤器的原理可以简单理解为用多个哈希函数判断一个 key 是否存在可能存在误判但不存在一定不会误判所以它能挡住大部分非法 key。缓存空值时要注意不能真的把空值无限缓存过期时间建议设置得很短。5.3 缓存与数据库一致性方案面试里比较难的问题落到“缓存和数据库怎么保持一致”。首先要明确业务上很少有绝对强一致的缓存需求多数场景接受最终一致。常见方案是“先更新数据库再删除缓存”。为什么不是先删缓存再更新数据库因为并发下可能出现线程 A 删了缓存线程 B 读到旧数据并写回缓存然后线程 A 才更新数据库最终缓存里一直是旧数据。“先更新数据库再删除缓存”也不是绝对没问题。如果删除缓存失败就会一直读到旧值。解决办法之一是延迟双删1. 删除缓存 2. 更新数据库 3. 休眠一小段时间比如 500ms 4. 再次删除缓存延迟双删不能保证所有极端情况都一致因为第二次删除和请求写入缓存之间仍有窗口期。更可靠的做法是订阅数据库 binlog比如通过 Canal 监听变更在变更发生后异步删除或更新缓存。这样可以把缓存更新和业务代码解耦也能减少业务代码里手动删缓存的遗漏。这里要记住一个原则缓存一致性没有银弹。面试时不要宣称某种方案能百分百一致而是要分析业务能容忍多长的数据延迟再给出对应方案。6. 分布式场景幂等、分布式锁和消息可靠性6.1 接口幂等设计与去重表分布式场景下常用的一个重要概念是幂等。幂等是指同一个请求执行一次和执行多次对系统的影响是一样的。支付回调、下单、消息消费都是典型场景。如果消息队列消费者收到重复消息而业务接口没有做幂等就会出现重复订单、重复扣款。常见幂等方案如下方案实现思路适用场景唯一请求 ID客户端生成 UUID服务端通过 Redis 判断是否处理过对外接口数据库唯一约束用订单号或业务号建唯一索引插入冲突时捕获异常核心写入状态机订单状态按流程流转已支付状态不能再次支付状态型业务去重表独立建一张去重表用业务唯一键做唯一索引消息消费者以支付回调为例public void handlePayCallback(PayCallbackDTO dto) { // dto 中携带业务订单号和事件类型 String businessId dto.getOrderId() _ dto.getEventType(); try { int count payCallbackMapper.insertIgnore(new PayCallbackRecord(businessId)); if (count 0) { // 重复消息直接返回 return; } // 继续处理业务 orderService.markPaid(dto.getOrderId()); } catch (DuplicateKeyException e) { log.warn(duplicate pay callback, orderId{}, dto.getOrderId()); } }这个例子的核心是业务唯一键插入成功代表这条消息首次进入处理流程插入失败代表之前已经处理过直接忽略。6.2 分布式锁的选型和实现单机环境可以用 synchronized 或 ReentrantLock但分布式环境下多个进程需要共享锁就要用分布式锁。分布式锁需要满足几个基本条件互斥性同一时刻只有一个客户端能持有锁。防死锁客户端异常退出后锁能自动释放。锁不能被误删只能释放自己持有的锁。合理续期任务执行时间不能超过锁的过期时间。Redis 实现分布式锁最常见的命令是SET lock_key request_id NX PX 30000这里要解释两个参数NX 表示只有 key 不存在时才能设置成功PX 表示过期时间防止客户端宕机导致死锁。request_id 用于释放锁时验证归属避免误删其他人的锁。释放锁必须用 Lua 脚本保证检查和删除是一个原子操作if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end面试时有一个常见错误回答直接把加锁写成SETNX然后EXPIRE两条命令中间客户端崩溃锁没有过期时间就会死锁。正确写法是使用一条命令同时设置。如果业务执行时间可能超过锁过期时间就要做锁续期。简单做法是定时续期比如每隔锁超时时间的 1/3 续期一次。生产环境可以借助 Redisson 的看门狗机制但面试时只要能说清续期目的就可以了。对比不同实现方案优点缺点Redis 分布式锁性能好实现简单集群模式下存在极端一致性风险ZooKeeper 临时顺序节点可靠性高无过期时间问题性能相对低依赖 ZooKeeper数据库唯一索引实现最简单性能弱容易影响数据库6.3 消息队列消费语义与顺序问题消息队列的面试题通常围绕 Kafka、RocketMQ 等展开常见问题有三个重复消费、顺序消费、分布式事务。重复消费在大多数消息队列里是默认存在的情况因为消息队列通常采用 at least once 语义即至少投递一次网络超时、消费者宕机都会导致重复。应对方式不是让消息队列保证不重发而是让消费端做到幂等。上面提到的去重表在这里同样适用。顺序消费更考验细节。如果订单创建的多个消息分别进入不同分区消费者可能先处理了“支付完成”再处理“订单创建”业务就错了。解决思路通常有两个把同一业务实体的消息发送到同一个队列或同一个分区比如按订单号取模。在消息体中携带一个业务序号消费端按序号排序后再处理。分布式事务不是简单使用一个事务注解就能完成。常见方案是本地消息表和事务消息业务操作和本地消息表写入放在同一个本地事务中。定时任务或消息队列把消息发给下游。下游消费成功后确认。如果失败消息队列重试或定时任务扫描补偿。RocketMQ 的事务消息本质上也是“先发半消息本地事务成功后再 commit 投递”的思路目的是让本地事务和消息发送保持一致。面试时不需要把每种消息队列都背下来但要能说出最终一致的基本链路和补偿机制。7. 现场答题策略与考前自查清单7.1 项目描述怎么组织才有说服力Java 后端面试的“项目介绍”环节看起来开放其实最容易被问出深度。候选人常犯的错误是把项目从登录注册功能开始流水账式讲完面试官听完也不知道你的难点在哪。建议按这个顺序组织项目描述业务背景项目给谁用解决什么业务问题。我的职责负责哪些模块写了哪些关键功能。技术难点遇到了什么性能问题、一致性风险或架构选型问题。方案与比较为什么选择这个技术方案换一个方案会有什么问题。验证结果上线后效果如何比如接口耗时、数据量、异常率变化。项目里如果没有确定的量化数据不要编造。可以说“接口从原来的多次查询优化成一次查询压测后 QPS 从 800 提升到 1500”这类有依据的对比。如果没做过压测就说“减少了查询次数从原来的 3 次改为 1 次”这种描述不会引起反感和追问风险。7.2 遇到不会的题如何把损失降到最低面试中遇到不会的问题很正常重要的是不要慌。最差的做法是沉默或者靠猜硬编这会直接让面试官怀疑你的沟通能力。推荐的回答方式分三步坦诚边界直接说“这个方向我没有在项目里深入用过”。给出关联知识把自己了解的相近原理说出来。比如被问到没用过的框架可以说“虽然我没用过但它和 xxx 的原理相似我了解 xxx 是这样做”。给出解决问题的思路说清楚如果要我快速上手我会先看官方文档、确认版本、写最小用例验证再结合日志和监控排查。面试官更看重候选人的解决问题的思路而不是要求万能答案。能把自己的无知范围说清楚本身也是工程师能力的一部分。7.3 考前自查清单面试前一天不适合再刷新知识点适合做清单式自查。每项都能说出来就可以安心睡个好觉。模块重点确认项自查结果Java 集合HashMap 扩容、并发问题、ConcurrentHashMap 锁粒度能画图说明Java 并发volatile 语义、synchronized、线程池参数、AQS能写出 DCL 示例JVM内存区域、GC 算法、OOM 类型、jstack/jstat 用途能说出排查顺序SpringBean 生命周期、三级缓存、事务失效、自动配置能举例说明失效场景MySQL索引结构、最左前缀、隔离级别、EXPLAIN 关键列能分析一条慢 SQLRedis数据结构场景、三大缓存问题、一致性方案能给出伪代码分布式幂等设计、分布式锁、消息重复消费和顺序问题能对比方案优缺点如果某个模块有明显空白不要试图考前硬塞。把最基础的定义和一条使用场景记住再准备一句坦诚的“这块我还不熟”也可以。7.4 接下来的复习路径面试准备是一个持续迭代的过程不是刷完题单就结束。建议把复习分成三层第一层是高频知识框架也就是上面这些模块。目标是让自己对每个常见问题都能说出一段完整逻辑而不是背诵结论。第二层是源码与原理。把一个类或一个机制的源码看透比如 HashMap 的 put 流程、Spring Bean 的创建流程、MyBatis 的 Mapper 代理生成这些是最能体现技术深度的部分。第三层是项目复盘和模拟面试。每周安排一次模拟面试让对方随机从高频清单里提问重点练表达流畅度和追问反应速度。面试官经常会顺着回答往下追问平时练习时也要要求自己多答两层。真正能提升通过率的不是“答案背得熟”而是面对一个不熟悉的问题时能通过已有知识搭建出合理分析路径。后端面试考察的是长期积累和思考方式把高频考点连成网络比临时抱佛脚刷一百道题更值得投入。

相关新闻

2026/8/31 4:12:46

【MySQL篇】事务的认识以及四大特性

何为事务?-----*事务(Transaction)是指一组操作的集合,这些操作要么全部执行成功,要么全部不执行。事务通常用于保证数据库的一致性、完整性和可靠性,确保数据的完整性与正确性。有效避免部分执行&#xff…

2026/8/31 4:12:46

【MySQL】链接池原理:简单理解网站的数据流动

前言:本节内容是博主快速上手mysql专栏的最后一篇文章。 本节内容不讲解mysql语法了, 而是以网站为例,讲一下前后端的逻辑与联系。 友友们可以当成一个故事听。> > ps:本节不需要门槛, 当成一个故事听即可!*目录…

2026/8/31 4:12:46

从GLM-5.3-Flash到MHS标准:大模型成本与信任的双线竞争

早上打开 BestBlogs 早报,08-28 这一期被我扫到了两条:一条是 GLM-5.3-Flash 发布,另一条是 Anthropic 推进 MHS 标准。单看标题,这两句话都可以当普通的行业动态刷过去——每天都有模型发布,每天都有公司说自己在做标…

2026/8/31 4:27:46

原神愚人众评议会解析:极光与冻土的至冬路线之争

1. 这篇文章真正要解决的问题如果你是《原神》的剧情党,或者是对愚人众执行官这一组织设定感兴趣的玩家,最近一定注意到了一类讨论:当「公鸡」普契涅拉出场主持皇都评议会,让「公子」达达利亚和「富人」潘塔罗涅围绕“极光与冻土”…

2026/8/31 4:27:46

网易Java校招笔试复盘:从集合框架到JVM的考点地图

秋招季有人把网易2020校招笔试的Java开发工程师正式批试题翻出来复盘,我顺手把当年的笔记也翻了一遍。说实话,这套卷子在过去几年里不断被当成“Java八股文”素材流传,面试题、基础题、容器题、算法题都被拆得七零八落。但我重新过了一遍之后…

2026/8/31 4:27:46

CSDN技术文章-巴氏杀菌直接蒸汽注入与间接夹套加热对比

食品巴氏杀菌设备的两种加热路线:直接蒸汽注入 vs 间接夹套加热 文章标签:工业设备 / 食品工程 / 热处理 / 蒸汽系统引言 巴氏杀菌是食品工业最常见的热处理工艺之一,但很多人对"杀菌设备怎么加热"这件事的理解停留在"加热就行…

2026/8/31 4:27:46

爱奇艺2019秋招测试开发笔试题B卷考点解析

又到一年秋招季,后台收到不少读者留言问测试开发方向到底怎么准备笔试,这让我想起手头一直存的一套老题——爱奇艺2019秋招测试开发方向笔试题(B)。之所以一直留着这套题,是因为它在大厂测试开发笔试里非常有代表性&am…

2026/8/31 4:27:46

LeetCode 412 Fizz Buzz:从基础解法到可扩展设计的编程实践

这次我们来看一个经典的编程面试题:LeetCode 第 412 题 Fizz Buzz。这题本身不复杂,但它像一面镜子,能照出你写代码的基本功、对边界条件的处理,以及代码的可读性和扩展性。很多面试官喜欢用它来开场,因为它能快速判断…

2026/8/31 4:22:46

从零构建AI Agent:OpenClaw 部署、Skill机制与IM机器人实战

今年团队在推进 AI Agent 落地时,围绕 OpenClaw 做了不少从零到一的构建工作。从最初选型、本地部署、模型接入,到后来接 IM、写 Skill、调权限,整个过程踩了不少坑,也沉淀出了一套相对完整的工程方法。网上的资料大多比较零散&am…

2026/8/31 1:05:20

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/8/31 2:14:20

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/8/31 1:41:28

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/8/31 0:07:32

STM32C5设备支持包(IAR DFP)安装指南与常见坑

上一阵子在IAR里折腾一块基于STM32C5系列的新板子,工程从STM32CubeMX导出来之后怎么都编译不过。报错信息很干脆:找不到设备描述文件。跟着错误路径去查,发现指向的是一个让我愣了一下的名字:STMicroelectronics.stm32c5xx.2.1.0.…

2026/8/31 0:07:32

STM32N657 SWO引脚矛盾:CubeMX显示PB3,数据手册为PB5

拿到STM32N657这颗料的第一天,我就撞上了一个让人原地懵圈的引脚矛盾:CubeMX里清清楚楚显示SWO在PB3,翻开数据手册的引脚说明表,却赫然写着PB5。对于一个靠SWO输出调试日志吃饭的人而言,这种"工具和手册打架"…

2026/8/28 16:16:48

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/28 16:16:50

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/28 11:06:45

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…