一、面试
1、类加载的过程
类加载是 JVM 将类的字节码加载到内存并转换为可用 Class
对象的过程,分为五个阶段。加载 阶段通过类加载器(如引导类加载器、扩展类加载器)读取字节码并生成 Class
对象;验证 阶段确保字节码符合 JVM 规范,包括文件格式、语义检查等;准备 阶段为静态变量分配内存并赋予默认值(如 int
初始化为 0
);解析 阶段将符号引用(如方法名)转换为直接引用(内存地址);初始化 阶段执行类构造器 <clinit>
方法,完成静态变量赋值和静态代码块执行。整个过程确保类的正确性和安全性。
2、双亲委派机制
双亲委派机制是类加载的核心规则,其流程为:当收到类加载请求时,先委派给父类加载器处理,仅当父类无法完成(如找不到类)时,子类加载器才尝试自己加载。这一机制通过分层隔离(如引导类加载器优先加载核心类)避免了重复加载和核心类被篡改的风险。典型应用场景包括 Tomcat 为每个 Web 应用提供独立类加载器以防止类冲突,JDBC 驱动通过线程上下文类加载器打破双亲委派以加载不同数据库驱动,以及热部署框架动态替换类时绕过双亲委派。
3、打破双亲委派机制的场景
双亲委派机制并非绝对,某些场景需要打破它以满足需求。例如,Tomcat 的 Web 应用类加载器优先加载 WEB-INF/classes
下的类,而非委派给父加载器,从而实现应用隔离;JDBC 驱动通过线程上下文类加载器(TCCL)加载具体数据库驱动类,避免因双亲委派导致只能加载一个驱动;热部署框架(如 OSGi)动态替换类时,需绕过父加载器直接由子加载器加载新版本类,实现无重启更新。
4、synchronize 和 ReentrantLock 的区别
✅synchronized和reentrantLock区别?
synchronized
是 JVM 内置锁,基于监视器(Monitor)实现,支持非公平锁且不可中断,仅提供单一条件变量(wait/notify
)。其优势是使用简单,由 JVM 自动释放锁,但缺乏灵活性。ReentrantLock
是 JDK 提供的 API,基于 AQS 实现,支持公平/非公平锁、可中断锁(lockInterruptibly()
)、多条件变量(newCondition()
),且需手动释放锁。性能上,优化后的 synchronized
与 ReentrantLock
接近,但后者在高并发场景下可通过条件变量实现更精细的线程控制。
5、MVCC 的作用
MVCC(多版本并发控制)通过保存数据历史版本,实现非锁定读(快照读),提升并发性能。其核心是为每行数据维护隐藏字段(如事务 ID DB_TRX_ID
和回滚指针 DB_ROLL_PTR
),记录修改事务和版本链。读操作基于一致性视图(ReadView)过滤不可见版本,避免加锁阻塞写操作,而写操作通过生成新版本实现无锁并发。MVCC 是 MySQL InnoDB 在可重复读隔离级别下解决幻读的关键机制。
6、可重复读如何通过 MVCC 实现
在可重复读隔离级别下,MVCC 通过事务开始时生成的一致性视图(ReadView)保证多次读取结果一致。ReadView 记录当前活跃事务 ID,查询时通过版本链和可见性规则判断数据版本:若数据版本的事务 ID 小于 ReadView 的最小活跃事务 ID,或等于当前事务 ID,则可见。例如,事务 A 在读取期间,事务 B 修改了数据但未提交,事务 A 基于版本链读取事务 A 自身修改前的版本,从而避免脏读和不可重复读。
7、Spring AOP 的实现原理
Spring AOP 基于动态代理实现切面编程,支持 JDK 动态代理(基于接口)和 CGLIB(基于继承)。核心流程包括:定义切面(Aspect)和通知(Advice),通过 ProxyFactory
创建代理对象,在目标方法调用前后织入增强逻辑(如日志、事务)。JDK 代理通过 InvocationHandler
拦截接口方法,CGLIB 通过生成目标类的子类覆盖方法并插入切面代码。AOP 在运行时动态生成代理对象,对原对象行为进行增强,实现解耦和代码复用。
8、ThreadLocal 的作用及使用场景
ThreadLocal 为每个线程提供独立变量副本,避免共享数据竞争。其核心是通过 ThreadLocalMap
存储线程本地变量,每个线程访问自己的副本。典型场景包括:数据库连接、用户会话(如 SecurityContext)的线程隔离,分布式事务中存储 XID。需注意手动调用 remove()
清理数据,防止线程池中线程复用导致内存泄漏。例如,在 Web 应用中,通过 RequestContextHolder
存储请求上下文,确保每个请求线程独立访问请求参数。
9、分布式锁的实现方式
分布式锁用于多节点环境下协调资源访问,常见实现方式有三种:数据库 通过唯一索引插入记录获取锁,删除记录释放锁,但存在单点故障和性能问题;Redis 利用 SET key value NX EX 10
原子命令实现锁,结合 Lua 脚本保证原子性,需处理锁续期(如 Redisson 的 WatchDog 机制);ZooKeeper 通过创建临时有序节点,监听前序节点删除事件实现锁,可靠性高但性能较低。选择时需权衡一致性、性能和复杂度。
10、设计模式的种类
设计模式分为三类共 23 种(GoF):创建型 关注对象创建机制,包括单例(保证唯一实例)、工厂方法(延迟子类决定实例化)、抽象工厂(创建产品族)、建造者(分步构造复杂对象)、原型(克隆对象);结构型 关注类与对象组合,如适配器(接口转换)、装饰器(动态扩展功能)、代理(控制访问)、组合(树形结构);行为型关注对象间通信,如观察者(事件通知)、策略(算法替换)、模板方法(定义骨架)、责任链(请求分发)。
11、分布式事务的实现方式
分布式事务通过跨服务数据一致性方案解决,主要分为强一致性和最终一致性两类。强一致性 方案包括:2PC (两阶段提交)通过协调者统一提交/回滚,但存在同步阻塞问题;TCC (Try-Confirm-Cancel)通过业务补偿实现,需编写 Try、Confirm、Cancel 接口。最终一致性 方案包括:本地消息表 (如 RocketMQ 事务消息)将业务操作与消息记录本地提交,异步消费消息;Saga 模式将长事务拆分为多个本地事务,通过补偿操作回滚。选择时需根据业务容忍度权衡一致性与复杂度。
12、分库分表的区分方式
分库分表是解决单库性能瓶颈的两种策略:垂直分库 按业务拆分(如订单库、用户库),解决资源竞争和扩展性问题,但需处理跨库事务;水平分表 按规则(哈希、范围、时间)拆分单表数据,解决单表数据量过大导致的查询效率低下。例如,用户表按 user_id
哈希分 10 张表,分散存储压力。两者常结合使用(如先垂直再水平),需注意数据分布均匀性和跨分片查询优化。
13、数据倾斜的治理方式
数据倾斜指计算节点负载不均,需针对性优化。识别热点 Key 可通过采样或监控工具定位(如 Spark UI);加盐打散 对 Key 追加随机后缀(如 key_1
、key_2
),分散计算后聚合结果;两阶段聚合 先局部聚合(带盐值)再全局聚合(去盐值);调整数据分布通过随机前缀或哈希分片均衡负载。例如,大表关联时对倾斜 Key 单独处理,避免 Shuffle 阶段数据堆积。
14、MySQL 的治理过程
MySQL 治理涵盖架构、优化、监控与高可用。架构层 通过主从复制、读写分离分担压力,分库分表解决单表瓶颈;优化层 包括索引优化(覆盖索引、最左前缀匹配)、SQL 优化(避免全表扫描、减少临时表);监控层 利用慢查询日志定位低效 SQL,Percona Monitoring 分析性能指标,pt-query-digest 解析执行计划;备份 结合物理备份(xtrabackup)和逻辑备份(mysqldump);高可用通过 MHA 自动故障转移或 GTID 实现主从切换,保障服务连续性。
二、力扣
1、乘积最大子数组

java
class Solution {
public int maxProduct(int[] nums) {
int n=nums.length;
int[][] dp=new int[n][2];
dp[0][0]=nums[0];
dp[0][1]=nums[0];
int res=nums[0];
for(int i=1;i<n;i++){
dp[i][0]=Math.min(dp[i-1][0]*nums[i],Math.min(dp[i-1][1]*nums[i],nums[i]));
dp[i][1]=Math.max(dp[i-1][1]*nums[i],Math.max(dp[i-1][0]*nums[i],nums[i]));
res=Math.max(res,dp[i][1]);
}
return res;
}
}
2、二叉树最大宽度

java
class Solution {
Map<Integer,Long> map;
int res;
public int widthOfBinaryTree(TreeNode root) {
map=new HashMap<>();
dfs(root,1,0);
return res;
}
public void dfs(TreeNode root,int dep,long point){
if(root==null) return;
if(!map.containsKey(dep)){
map.put(dep,point);
}
long left=map.get(dep);
res=Math.max(res,(int)(point-left)+1);
dfs(root.left,dep+1,point*2);
dfs(root.right,dep+1,point*2+1);
}
}
3、打家劫舍

java
class Solution {
public int rob(int[] nums) {
int n=nums.length;
int[][] dp=new int[n][2];
dp[0][1]=nums[0];
for(int i=1;i<n;i++){
dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]);
dp[i][1]=dp[i-1][0]+nums[i];
}
return Math.max(dp[n-1][0],dp[n-1][1]);
}
}
4、根据身高重建队列

java
class Solution {
public int[][] reconstructQueue(int[][] people) {
// 身高从大到小排(身高相同k小的站前面)
Arrays.sort(people, (a, b) -> {
if (a[0] == b[0]) return a[1] - b[1];
return b[0] - a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for (int[] p : people) {
que.add(p[1],p);
}
return que.toArray(new int[people.length][]);
}
}