华兴致远 Java开发 二面

#JAVA##JAVA面经##JAVA内推#

1. Redis 分布式锁在主从切换时为什么会出现锁丢失?

思路 核心是主从异步复制:主节点加锁成功但未同步到从节点,主宕机后从升主,新主无锁数据,导致多个客户端重复加锁。

回答示例

Redis 主从架构下锁丢失的核心原因是主从数据异步复制

  1. 客户端 A 向主节点发起 SET NX 加锁,主节点加锁成功;
  2. 主节点还没来得及把“锁数据”同步到从节点,主节点宕机;
  3. 哨兵/集群触发主从切换,从节点升级为新主节点,但新主节点没有客户端 A 的锁数据;
  4. 客户端 B 向新主节点加锁,成功获取到同一把锁,导致锁丢失、分布式锁互斥性被破坏。

解决方案:用 Redisson 红锁(多实例加锁)、或 Redis Cluster 仲裁机制,避免单主依赖。

2. MyBatis 预编译底层是如何防止 SQL 注入的?

思路 核心是 JDBC PreparedStatement 预编译:参数与 SQL 模板分离,参数被当作“值”而非“SQL 指令”解析。

回答示例

MyBatis 预编译防注入的核心依赖 JDBC 的 PreparedStatement,底层逻辑:

  1. #{} 会把 SQL 模板(如 select * from user where id = ?)发送给数据库,数据库先编译这个模板,生成执行计划;
  2. 把参数值(如 1 or 1=1)传入编译后的模板,数据库只会把参数当作“纯字符串值”处理,不会解析成 SQL 指令;
  3. 而 ${} 是直接拼接字符串,参数中的 or 1=1 会被当作 SQL 指令执行,因此会产生注入。

简单说:预编译让“SQL 结构”和“参数值”彻底分离,参数无法改变 SQL 执行逻辑,从而杜绝注入。

3. InnoDB 可重复读是如何通过 MVCC 实现一致性视图的?

思路 核心是事务启动时生成 ReadView,通过 undo log 版本链读取符合版本的记录,不读取事务启动后的新数据。

回答示例

InnoDB 的 MVCC 实现可重复读的核心是 ReadView(一致性视图)+ 版本链

  1. 每个事务启动时,会生成一个 ReadView,包含三个核心值:
    • m_ids:当前活跃的事务 ID 列表;
    • min_trx_id:活跃事务最小 ID;
    • max_trx_id:下一个要分配的事务 ID;
  2. 每行数据都有隐藏列:trx_id(修改该记录的事务 ID)、roll_pointer(指向 undo log 版本链);
  3. 事务读取数据时,会遍历版本链,找到第一个满足“事务 ID < min_trx_id 或 事务 ID 不在 m_ids 中”的记录(即事务启动前已提交的数据);
  4. 整个事务期间,ReadView 不会变化,因此多次读取同一数据,都会拿到同一版本,实现“可重复读”。

4. synchronized 偏向锁在 JVM 里是怎么标记线程 ID 的?

思路 存储在对象头 MarkWord 中:偏向锁模式下,MarkWord 会记录偏向的线程 ID + 偏向锁标记位。

回答示例

偏向锁的线程 ID 存储在对象头的 MarkWord 区域,具体逻辑:

  1. 对象创建时,MarkWord 的偏向锁标记位为 1、锁状态位为 01,此时线程 ID 为 0(无偏向);
  2. 第一个线程获取偏向锁时,JVM 会通过 CAS 把该线程的 ID 写入 MarkWord 的“线程 ID 字段”,同时设置偏向时间戳;
  3. 后续该线程再次获取锁时,只需对比 MarkWord 中的线程 ID 是否与自身一致:一致则直接获取锁,无需 CAS/自旋;
  4. 若有其他线程竞争,偏向锁会撤销,升级为轻量级锁。

简单说:偏向锁把“专属线程 ID”写在对象头,通过 ID 匹配快速加锁,减少无竞争时的开销。

5. 轻量级锁自旋失败后为什么会膨胀为重量级锁?

思路 自旋消耗 CPU,且自旋次数有限,持续自旋会导致 CPU 空耗;重量级锁让线程阻塞,释放 CPU 资源。

回答示例

轻量级锁自旋失败膨胀为重量级锁的核心原因是避免 CPU 空耗

  1. 轻量级锁的自旋是“忙等”:线程在用户态循环尝试 CAS 加锁,不释放 CPU 资源;
  2. JVM 对自旋次数有限制(默认 10 次或自适应自旋):若自旋次数耗尽仍未获取锁,说明竞争激烈;
  3. 此时继续自旋会大量消耗 CPU(多个线程空等),因此 JVM 会把轻量级锁膨胀为重量级锁:
    • 调用 OS 内核的互斥锁(mutex),让失败的线程进入“阻塞态”,释放 CPU;
    • 当锁释放时,通过内核唤醒阻塞的线程,虽然上下文切换有开销,但避免了 CPU 空耗。

核心权衡:轻量级锁适合“短时间、低竞争”,重量级锁适合“长时间、高竞争”。

6. 装饰器模式在 JDK 里的典型应用是什么?

思路 核心示例:IO 流(BufferedReader、InputStream 系列),通过装饰器动态增强流的功能。

回答示例

JDK 中装饰器模式最典型的应用是 Java IO 流,比如:

  1. 基础流:FileInputStream(读取文件)、FileReader(字符读取);
  2. 装饰器流:(缓冲增强)、(读取基本类型

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏

全部评论
本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏。 专栏目录https://www.nowcoder.com/share/jump/1772859327707
点赞 回复 分享
发布于 03-16 21:20 广东

相关推荐

点赞 评论 收藏
分享
03-28 20:13
东南大学 Java
2的三次幂:第一个项目写的太简略了,可以结合业务背景写下细节,第二个太冗余了,缺乏量化指标,一眼玩具
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务