Java 面试题›MySQL 面试题›乐观锁与悲观锁怎么实现?
面试考察点
概念理解度:面试官不仅仅是想知道你听过这两种锁的名字,更是想知道你是否理解它们的核心思想差异—— “先加锁再访问” vs “先访问再加锁”。
实践应用能力:考察你是否在实际项目中使用过这两种锁,能否根据业务场景(读多写少 vs 写多读少)选择合适的锁策略。
问题诊断能力:是否了解乐观锁的 ABA 问题、悲观锁的死锁风险,以及各自的解决方案。
核心答案
对比维度
乐观锁
悲观锁
核心思想
假设冲突很少,不加锁,更新时检测冲突
假设冲突频繁,操作前先加锁
实现方式
version 字段条件更新,靠影响行数判断
SELECT … FOR UPDATE / FOR SHARE
冲突检测时机
提交更新时检测,失败则重试
读取时就加锁,冲突方阻塞等待
ABA 问题
version 方案天然不存在(版本号只增不减);用业务字段做条件可能有
不存在(强锁串行化)
风险/代价
冲突多时重试开销大、自旋浪费 CPU
吞吐低、死锁风险、where 不走索引会锁表
适用场景
读多写 ...
Java 面试题›MySQL 面试题›char 和 varchar 的区别?
面试考察点
基础掌握度:面试官不仅仅是想知道两个类型的字面区别,更是想知道你是否理解 MySQL 的存储机制,能否在实际表设计中做出正确选择。
性能意识:考察你是否清楚定长和变长类型在存储效率、查询性能、索引构建等方面的差异,以及在不同场景下的取舍。
实践经验:能否结合实际业务场景(如用户名、手机号、MD5 值等)给出合理的选择建议,而不是只会背概念。
核心答案CHAR 和 VARCHAR 是 MySQL 中两种最常用的字符串类型,核心区别如下:
对比维度
CHAR
VARCHAR
存储方式
定长存储,不足补空格
变长存储,按实际长度
长度范围
0 ~ 255 字符
0 ~ 65,535 字节
空间占用
固定占用声明长度
实际长度 + 1~2 字节前缀
尾部空格
存储时补齐,检索时移除
原样存储和检索
性能特点
更快的 CRUD(无长度计算)
更省空间(按需分配)
适用场景
长度固定的数据(手机号、MD5)
长度变化的数据(用户名、地址)
一句话总结:CHAR 是定长、 ...
Java 面试题›MySQL 面试题›什么是最左前缀匹配?为什么要遵守?
面试考察点
原理理解:面试官不仅仅想知道你会背 “最左前缀原则”,更想考察你是否理解联合索引的 B+ 树存储结构,能否从数据结构层面解释为什么必须遵循这个原则。
实践能力:是否能够正确设计联合索引的列顺序,避免索引失效,写出能走索引的高效 SQL。
问题排查:是否具备分析索引失效原因的能力,能否通过 EXPLAIN 定位最左前缀相关问题。
核心答案最左前缀匹配原则 是指:联合索引按照定义顺序从左到右依次匹配,查询条件必须从索引的最左列开始,并且不能跳过中间的列。
-- 联合索引INDEX idx_a_b_c (a, b, c)-- ✅ 能走索引WHERE a = 1WHERE a = 1 AND b = 2WHERE a = 1 AND b = 2 AND c = 3-- ❌ 不能走索引WHERE b = 2 -- 缺少最左列 aWHERE c = 3 -- 缺少 a 和 bWHERE b = 2 AND c = 3 -- 缺少最左列 a
为什么要遵守 ...
Java 面试题›MySQL 面试题›什么是数据库范式,为什么要反范式化设计?
面试考察点
理论基础:面试官不仅仅想知道你是否背过三范式,更想考察你是否理解范式设计的本质目的——消除数据冗余、保证数据一致性。
架构权衡:是否具备 “空间换时间” 的思维,能否在规范化与性能之间做出合理的取舍。
实践经验:是否在真实项目中应用过反范式设计,能否结合具体场景说明为什么要打破范式规则。
核心答案数据库范式 是一组关系数据库设计的规范,目的是消除数据冗余、避免更新异常。常见的有三大范式:
范式
核心规则
解决的问题
第一范式(1NF)
每个字段都是不可分割的原子值
消除重复组、保证原子性
第二范式(2NF)
在 1NF 基础上,非主键字段完全依赖主键
消除部分依赖、减少冗余
第三范式(3NF)
在 2NF 基础上,非主键字段不传递依赖主键
消除传递依赖、避免更新异常
反范式化设计 是在满足基本范式的前提下,通过引入冗余数据来提升查询性能,本质是 “空间换时间”。 一句话总结:范式保证数据一致性,反范式提升查询性能,实际项目中需要根据业务场景权衡选择。
深度解析一、三大 ...
Java 面试题›Java 集合面试题›ConcurrentHashMap 为什么在 JDK 8 中废弃分段锁?
面试考察点
架构演进理解:面试官不仅仅是想知道 “废弃了” 这个事实,更是想知道你是否理解分段锁的设计局限性,以及 JDK 1.8 新方案解决了哪些问题。
性能权衡思维:考察你是否理解锁粒度、内存开销、并发度之间的权衡,以及为什么 “更简单的 synchronized” 反而比 “复杂的 ReentrantLock” 更好。
JVM 优化认知:是否了解 JDK 1.6 后 synchronized 的锁升级机制,以及它如何改变了并发编程的最佳实践。
核心答案JDK 1.8 废弃分段锁的主要原因:分段锁并发度固定、内存开销大、实现复杂,而 CAS + synchronized 方案并发度更高、内存更省、实现更简洁: **** **** **** **** ****
对比维度
JDK 1.7 分段锁
JDK 1.8 CAS + synchronized
并发度
最多 16(Segment 数量)
等于桶数量(默 ...
Java 面试题›Java 集合面试题›ConcurrentHashMap 是如何保证线程安全的?
面试考察点
版本差异理解:面试官不仅仅是想知道 “用了锁” 这个结论,更是想知道你是否了解 JDK 1.7(分段锁)和 JDK 1.8(CAS + synchronized)两种截然不同的实现方式。
并发原理深度:考察你是否理解 CAS 无锁操作、synchronized 锁升级、volatile 可见性等底层机制,以及它们如何协同工作。
对比分析能力:能否说清楚 ConcurrentHashMap 与 Hashtable、Collections.synchronizedMap() 的区别,以及为什么性能更好。
核心答案ConcurrentHashMap 在 JDK 1.8 中通过 “CAS + synchronized + volatile” 三者协同保证线程安全: **** **** ****
机制
作用
应用场景
CAS
无锁更新
空桶插入、size 计数
synchronized
锁桶头节点
哈希冲突时的插入/更新 ...
Java 面试题›Java 集合面试题›什么是 fail-fast?什么是 fail-safe?
面试考察点
异常处理经验:面试官不仅仅是想知道这两个概念的定义,更是想知道你是否遇到过 ConcurrentModificationException,以及是否理解它的产生原因。
迭代器原理:考察你是否了解 Java 集合迭代器的工作机制,特别是 modCount 的检测机制。
并发安全意识:是否知道在多线程或单线程迭代时修改集合的正确姿势,以及 CopyOnWriteArrayList 等并发集合的应用。
核心答案fail-fast(快速失败) 和 fail-safe(安全失败) 是 Java 集合迭代器在遇到并发修改时的两种不同处理策略: **** **** **** **** **** ****
特性
fail-fast
fail-safe
行为
检测到修改立即抛异常
检测到修改不抛异常,继续迭代
异常
ConcurrentModificationException
无异常
迭代方式
直接遍历原集合
...
Java 面试题›Java 集合面试题›Set 如何保证元素不重复的?
面试考察点
底层实现理解:面试官不仅仅是想知道 “Set 不重复” 这个结论,更是想知道你是否了解 HashSet 底层是用 HashMap 实现的这一经典设计。
判断机制掌握:考察你是否理解判断元素重复的完整逻辑:先比 hashCode(),再比 equals(),以及为什么要这样设计。
不同实现对比:是否了解 HashSet、TreeSet、LinkedHashSet 保证不重复的不同机制。
核心答案以最常用的 HashSet 为例,它底层基于 HashMap 实现,元素作为 HashMap 的 key 存储,利用 key 的唯一性来保证元素不重复:
Set 实现类
底层结构
判重机制
特点
HashSet
HashMap
hashCode() + equals()
无序,查询 O(1)
LinkedHashSet
LinkedHashMap
hashCode() + equals()
保持插入顺序
TreeSet
TreeMap
compareTo() 或 Comparator
有序, ...
Java 面试题›Java 集合面试题›HashMap 的 hash 方法是如何实现的?
面试考察点
源码掌握:面试官不仅仅是想知道 “有个 hash 方法” 这个事实,更是想知道你是否读过源码,是否理解 (h = key.hashCode()) ^ (h >>> 16) 这行代码的精妙之处。
位运算理解:考察你是否理解无符号右移 >>> 和异或 ^ 的作用,以及为什么这样设计能减少哈希冲突。
设计思想:是否理解 “扰动函数” 的概念,以及高位参与运算对散列均匀性的影响。
核心答案HashMap 的 hash() 方法通过 “扰动函数” 将 key 的 hashCode() 高 16 位与低 16 位异或,让高位信息参与散列运算:
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
关键点
说明
null 处理
key 为 n ...
Java 面试题›Java 集合面试题›HashMap 在 get 和 put 时,底层流程是怎样的?
面试考察点
核心操作理解:面试官不仅仅是想知道 “put 存数据、get 取数据” 这个表面行为,更是想知道你是否理解底层的数据结构操作,包括哈希计算、桶定位、冲突处理、链表/红黑树遍历等。
源码掌握深度:考察你是否读过 putVal() 和 getNode() 的源码,是否理解扰动函数、树化条件、扩容触发等关键逻辑。
关联知识串联:能否将 hash 计算、equals 比较、扩容机制、红黑树转换等知识点串联起来,形成完整的认知体系。
核心答案HashMap 的 get 和 put 操作都遵循 “计算 hash → 定位桶 → 遍历处理” 的核心逻辑:
操作
核心流程
时间复杂度
put(key, value)
hash → 定位桶 → 查找/插入 → 更新 size → 检查扩容
O(1) ~ O(log n)
get(key)
hash → 定位桶 → 遍历查找 → 返回 value
O(1) ~ O(log n)
一句话总结:两者前半 ...
