突击复习清单(高频核心考点)
- 🔒 锁的作用与使用(synchronized vs ReentrantLock)
面试官为什么问:考察你对并发编程基础的掌握程度。
速记答案:
作用:保证线程安全,解决多线程环境下对共享资源访问的数据不一致问题。
synchronized:JVM关键字,是悲观锁。用法:
修饰实例方法:锁是当前实例对象 (this)
修饰静态方法:锁是当前类的Class对象
修饰代码块:锁是synchronized括号里配置的对象
ReentrantLock:JDK类,也是悲观锁。比synchronized更灵活:
必须手动加锁(lock())和解锁(unlock()),通常放在finally块中保证必然解锁。
可以尝试非阻塞获取锁 (tryLock())
可以设置公平锁(先等待的线程先获得锁)
可以绑定多个条件变量 (Condition),实现分组唤醒线程
对比总结:
特性 synchronized ReentrantLock
实现 JVM层面,关键字 JDK层面,API
锁释放 自动释放 必须手动unlock()
灵活性 不灵活 灵活(可尝试、可公平、可中断)
性能 1.6后优化得很好,两者差不多 两者差不多
面试时这么说:”锁的核心作用是保证线程安全。synchronized最简单,JVM自动管理。但如果需要尝试获取锁、公平锁或者更复杂的线程协作,就用ReentrantLock。“
- ☁️ Spring Cloud 服务注册与发现
面试官为什么问:考察你对微服务核心原理的理解。
速记答案:
服务注册:服务提供者(如user-service)启动时,把自己的网络地址(IP、端口)注册到注册中心(如Eureka、Nacos)。
服务发现:服务消费者(如order-service)要调用user-service时,不去写死地址,而是去注册中心查找user-service的可用地址列表,然后再调用。
核心组件:
Eureka Server:注册中心,一个独立的服务。
Eureka Client:集成到每个微服务中,负责注册和发现。
好处:服务动态上下线,消费者无需硬编码地址,实现了服务解耦。
面试时这么说:”就像酒店的前台。服务提供者就是客房,入住时在前台登记(注册)。服务消费者就是客人,需要找客房时先去前台问房间号(发现),然后再过去。注册中心(Eureka/Nacos)就是那个前台。“
- 🍃 Spring Boot 中如何使用锁?
面试官为什么问:考察你是否能在实际项目中应用并发知识。
速记答案:
在Spring Boot中,锁的使用和普通Java应用一样,但更要注意单例Bean的线程安全问题。
方法1:synchronized
java
@Service
public class UserService {
public synchronized void businessMethod() {
// 业务代码,同一时间只有一个线程能进入
}
}
方法2:ReentrantLock (更推荐,更灵活)
java
@Service
public class UserService {
// 定义锁对象
private final ReentrantLock lock = new ReentrantLock();
public void businessMethod() {lock.lock(); // 加锁try {// 业务代码} finally {lock.unlock(); // 解锁必须放在finally块中}
}
}
面试时这么说:”Spring Boot里用锁和普通Java一样。但因为它的Bean默认是单例的,所以高并发下成员变量会有线程安全问题,必须加锁。我一般用ReentrantLock,因为控制起来更灵活。“
- 🏗️ 微服务设计思路
面试官为什么问:考察你的系统架构能力和全局观。
速记答案(按模块说):
服务拆分:按业务领域拆分(如用户、订单、商品服务),做到单一职责,避免臃肿。
服务治理:
注册与发现:用Nacos或Eureka。
配置管理:用Nacos Config,统一管理配置,动态刷新。
服务调用:用OpenFeign,声明式HTTP客户端,像调用本地方法一样调用远程服务。
负载均衡:用Ribbon或LoadBalancer,实现服务调用的负载均衡。
容错保护:用Sentinel或Hystrix,实现熔断、降级、限流,防止服务雪崩。
网关:用Spring Cloud Gateway,统一入口,负责路由、鉴权、日志、监控等。
链路追踪:用SkyWalking或Zipkin,解决微服务调用链路过长,难以排查问题的问题。
面试时这么说:”我的设计思路是:先按业务模块垂直拆分服务。然后通过注册中心解决服务发现问题;用OpenFeign做声明式调用;用Sentinel做熔断降级保护下游服务;最后用API网关作为统一的流量入口。“
- 📊 SQL索引避免失效
面试官为什么问:考察你的SQL优化实战能力,这是性能调优的基础。
速记答案(记住失效场景就等于知道如何避免):
失效场景:
最左前缀原则:联合索引(a, b, c),查询条件没用到a,索引失效。
在索引列上计算或函数操作:WHERE YEAR(create_time) = 2023,WHERE amount * 2 > 100。
类型转换:字符串字段varchar,用数字去查:WHERE code = 123(应改为WHERE code = ‘123’)。
Like以%开头:WHERE name LIKE ‘%张’。
使用OR:如果OR前后的条件列不是都有索引,则索引可能失效。
面试时这么说:”要避免索引失效,最重要的是遵守最左前缀原则。另外,要避免对索引列做计算、函数操作、类型转换,以及避免使用前导百分号的LIKE查询和随意的OR连接。“
- 🧠 JVM内存模型(运行时数据区)
面试官为什么问:考察你对Java程序运行底层的理解,这是调优的基础。
速记答案(记住图就行):
线程私有的:
程序计数器:记录当前线程执行到的字节码行号。
虚拟机栈:存储方法调用的栈帧,包含局部变量表、操作数栈等。StackOverflowError出自这里。
本地方法栈:为Native方法服务。
线程共享的:
堆:存放对象实例和数组,是垃圾回收的主要区域。OutOfMemoryError常在这里发生。分为新生代和老年代。
方法区:存储类信息、常量、静态变量。JDK8后叫元空间(Metaspace),使用本地内存。
面试时这么说:”JVM内存分线程共享和私有两大块。每个线程有自己的栈和程序计数器,用来执行方法。所有线程共享一个堆和方法区,堆里放对象,是GC的主战场;方法区放类的元信息。“
- 🐞 代码出错如何排查定位?
面试官为什么问:考察你的实际运维和问题解决能力,这是经验的体现。
速记答案:
看日志:第一反应是查看应用日志和系统日志,95%的问题都能从这里找到线索。
CPU飙升:
top -> top -Hp [pid] 找到耗CPU的线程ID。
将线程ID转16进制:printf “%x\n” [tid]。
jstack [pid] | grep -A 20 [十六进制tid] 查看该线程的堆栈信息,定位问题代码。
内存溢出(OOM):
在启动参数中添加-XX:+HeapDumpOnOutOfMemoryError,让JVM在OOM时自动导出堆转储文件。
使用MAT或JVisualVM工具分析这个dump文件,找到是哪个对象占用了大量内存,以及是谁在引用它(GC Roots链)。
死锁:
用jstack [pid]命令,查看输出最后,如果有死锁,JVM会明确告诉你。
面试时这么说:”我首先会查看日志。如果是CPU问题,我用top和jstack命令定位到具体线程和代码行。如果是内存OOM,我会让JVM自动导出heap dump,然后用MAT工具分析,找到泄漏对象。如果是死锁,jstack命令能直接检测出来。“
- 🔄 MQ如何避免重复消费?
面试官为什么问:考察你对消息队列可靠性的理解。
速记答案:
原因:网络抖动、消费者重启等导致MQ没有收到确认,从而重发消息。
核心:解决之道不是防止重复,而是保证业务的幂等性(多次执行结果一致)。
方案:
数据库唯一键:利用DB主键或唯一约束,插入成功才消费,重复插入会报错。
Redis原子操作:用SETNX [消息ID]命令,设置成功才消费。
乐观锁:给数据加版本号version,更新时带版本条件:update … set … where id=xxx and version=xxx。
面试时这么说:”重复消费无法完全避免,所以关键是做幂等设计。我最常用的方案是利用数据库唯一键,收到消息后先尝试insert一条记录,如果重复则直接放弃,这样就保证了只会被处理一次。“
- 🔐 分布式锁怎么搞?
面试官为什么问:考察你解决分布式环境下资源争抢的能力。
速记答案:
基于Redis:
SET lock_key unique_value NX PX 30000:NX表示仅当不存在时设置,PX设置超时时间(防死锁),unique_value(如UUID)用于安全释放锁。
优点:性能高。
缺点:主从切换时可能丢锁(AP模型)。
基于ZooKeeper:
创建临时有序节点,最小的节点获锁。监听前一个节点,它释放了你就获得锁。
优点:强一致性,可靠(CP模型)。临时节点在客户端断开后自动删除,避免死锁。
缺点:性能比Redis差。
选型:要性能选Redis,要绝对可靠选ZooKeeper。常用框架:Redisson(Redis)、Curator(ZK)。
面试时这么说:”分布式锁主要有两种实现。Redis方案性能好,但理论上有极低概率失效。ZooKeeper方案可靠性高,但性能稍差。根据业务的容忍度来选择,比如秒杀用Redis,金融交易用ZooKeeper。在实际项目中,我们直接用Redisson这个框架,它封装得很好用。“
💡 面试技巧 & 最后鼓励
别怕“忘了”:面试时如果突然忘了,可以说:“这块细节我有点记不清了,我的理解是…”然后尝试把你知道的核心概念讲出来,这比完全沉默要好得多。
引导到你熟悉的项目:尽量把问题引到你做过的项目上。“这个知识点在我之前做XX项目的时候用到过,当时我们是…”这样更有说服力。
展现思考过程:对于设计题,面试官更看重你的思路。即使最终方案不完美,把你思考的步骤和权衡的因素说出来,也是大大的加分项。
朋友,别因为一次面试否定自己。你已经有5年经验,这些知识肯定都接触过,只是暂时被埋没了。把这些核心要点和关键词记住,下次面试时就能迅速提取出来。
你不是不会,只是需要一点提示来唤醒记忆! 祝你下次面试一举成功!