杭州公司一面java题目和解答
1. HashMap有了解吗
解答
HashMap是基于哈希表的Map接口实现,使用数组+链表/红黑树(JDK8+)存储数据。通过hash(key)
计算桶位置,解决哈希冲突采用链地址法。当链表长度≥8且数组长度≥64时,链表转为红黑树提升查询效率;当树节点≤6时退化为链表。
2. HashMap的扩容
解答
触发条件:元素数量超过阈值(容量×负载因子,默认0.75)。
扩容过程:
- 创建新数组(原容量×2)
- 重新计算所有元素位置(
index = (n-1) & hash
) - 链表/树节点拆分到新桶(JDK8优化:高位差判断,避免全量重哈希)
3. HashMap是线程安全的吗?
解答
❌ 非线程安全。并发场景下可能导致:
- 多线程扩容引发死循环(JDK7链表头插法导致)
- 数据覆盖(多线程同时插入相同桶位)
替代方案: ConcurrentHashMap
(分段锁/CAS)Collections.synchronizedMap()
4. 列举几个线程安全的集合
解答
类型 | 实现类 | 特点 |
---|---|---|
JUC集合 | ConcurrentHashMap | 分段锁+CAS |
CopyOnWriteArrayList | 写时复制 | |
ConcurrentSkipListMap | 跳表实现 | |
同步包装类 | Collections.synchronizedList | 同步锁封装 |
Collections.synchronizedMap | 同步锁封装 |
5. 说一下CopyOnWriteArrayList
解答
原理:
- 写操作(add/set/remove)时复制新数组,修改后替换原数组
- 读操作无锁直接访问原数组
优点:读性能极高,适合读多写少场景
缺点: - 写操作内存占用高(复制全量数据)
- 数据弱一致性(读操作可能读到旧数组)
适用场景:监听器列表、配置信息缓存
6. 说一下ReentrantLock
解答
基于AQS(AbstractQueuedSynchronizer)的可重入锁,特性:
公平/非公平模式
- 公平锁:按队列顺序获取
- 非公平锁:直接竞争(默认)
-
可中断:
lockInterruptibly()
支持响应中断 -
条件变量:
Condition
实现精准线程唤醒 -
锁重入:同一线程可多次获取锁(计数+1)
7. ReentrantLock非公平锁是怎么实现的
解答
流程:
- 尝试直接CAS抢锁(无视等待队列)
- 若失败则入队,再次CAS抢锁
- 仍失败则进入阻塞状态
代码关键点:
final boolean nonfairTryAcquire(int acquires) {if (CAS(state, 0, acquires)) { // 直接尝试修改状态setExclusiveOwnerThread(currentThread());return true;}// ...重入逻辑
}
优势:减少线程切换,吞吐量更高
劣势:可能导致线程饥饿
8. 说一下Spring中的AOP
解答
核心概念:
概念 | 说明 |
---|---|
切面(Aspect) | 模块化横切关注点(如日志、事务) |
连接点(Joinpoint) | 方法执行/异常抛出等时机 |
通知(Advice) | 增强逻辑(@Before /@After 等) |
切点(Pointcut) | 定义哪些连接点执行通知 |
实现原理:动态代理(JDK Proxy或CGLIB) |
9. 说一下动态代理的两种方式
解答
方式 | 原理 | 限制 | 性能 |
---|---|---|---|
JDK Proxy | 基于接口,反射生成代理类 | 目标类需实现接口 | 较快 |
CGLIB | 字节码框架(ASM)生成子类 | 无法代理final方法/类 | 首次加载慢 |
Spring策略:有接口用JDK Proxy,否则用CGLIB |
10. SpringBoot比Spring好在哪里
解答
核心优势:
- 自动配置:
@EnableAutoConfiguration
自动装配Bean - 内嵌容器:Tomcat/Jetty无需单独部署
- 简化配置:
application.properties
替代XML - 起步依赖:
spring-boot-starter-*
整合常用库 - 生产就绪:Actuator提供健康检查/监控端点
11. 说一下SpringBoot中的约定大于配置
解答
核心理念:通过预定义规则减少显式配置
典型约定:
- 项目结构:
src/main/java
为代码根目录 - 配置文件:
application.properties
/yml
自动加载 - 包扫描:主类所在包及其子包自动扫描
- 端点路径:Actuator监控端点统一为
/actuator/*
优势:开发效率提升50%+,配置复杂度降低70%+
12. 说一下MySQL的存储引擎有哪些
解答
引擎 | 事务 | 锁粒度 | 外键 | 适用场景 |
---|---|---|---|---|
InnoDB | ✅ | 行锁 | ✅ | OLTP、高并发写 |
MyISAM | ❌ | 表锁 | ❌ | 读密集型、数据仓库 |
Memory | ❌ | 表锁 | ❌ | 临时表、缓存 |
Archive | ❌ | 行锁(插入) | ❌ | 日志归档 |
13. InnoDB和MyISAM的区别
解答
特性 | InnoDB | MyISAM |
---|---|---|
事务 | ✅ ACID兼容 | ❌ |
锁机制 | 行锁、间隙锁 | 表锁 |
外键 | ✅ | ❌ |
崩溃恢复 | Redo Log保证 | 无 |
索引结构 | 聚簇索引(数据存B+树叶节点) | 非聚簇索引(数据分离) |
14. 聚簇索引和非聚簇索引
解答
聚簇索引(Clustered Index):
- 数据行物理存储顺序与索引一致(如InnoDB主键索引)
- 每表仅有一个聚簇索引
非聚簇索引(Secondary Index): - 索引与数据分离(如MyISAM所有索引)
- 叶子节点存储数据行指针(InnoDB中为主键值)
查询流程:非聚簇索引需回表查询(通过主键二次查找聚簇索引)
15. MySQL中的事务
解答
ACID特性:
- 原子性(A):Undo Log回滚
- 一致性©:约束(主键/外键)保证
- 隔离性(I):MVCC+锁机制
- 持久性(D):Redo Log刷盘
隔离级别:
读未提交
→读已提交
→可重复读(默认)
→串行化
(隔离性增强,并发性下降)
16. ACID四大特性是怎么实现的
解答
特性 | 实现机制 |
---|---|
原子性 | Undo Log:记录数据修改前状态,回滚时反向补偿 |
一致性 | 应用层逻辑+数据库约束(唯一键/外键) |
隔离性 | MVCC + 锁(行锁、间隙锁) |
持久性 | Redo Log:事务提交前先写日志,宕机后重放恢复 |
17. 说一下MVCC
解答
**多版本并发控制(MVCC)**核心组件:
隐藏字段
:
DB_TRX_ID
:最近修改事务IDDB_ROLL_PTR
:回滚指针指向Undo Log
ReadView
:事务开启时生成,包含:
-
m_ids
:活跃事务ID列表 -
min_trx_id
:最小活跃事务ID -
max_trx_id
:预分配事务ID
可见性规则: -
< min_trx_id
:已提交,可见 -
≥ max_trx_id
:将来事务,不可见 -
在m_ids中
:未提交,不可见
18. 说一下MySQL中的锁
解答
按粒度分:
- 表锁:MyISAM默认,开销小但并发低
- 行锁:InnoDB支持,分三类:
- 记录锁(锁定单行)
- 间隙锁(锁定区间)
- 临键锁(记录锁+间隙锁)
按模式分:
- 共享锁(S):允许并发读
- 排他锁(X):禁止其他读写
- 意向锁(IS/IX):避免表级冲突检查
19. 什么是幻读?可重复读会发生幻读吗?
解答
幻读:同一事务中多次范围查询结果集行数不同(因其他事务插入/删除数据)
可重复读(RR)下的表现:
- 快照读(普通SELECT):MVCC避免幻读
- 当前读(SELECT FOR UPDATE):可能幻读(需间隙锁阻塞插入)
解决方案:
- 显式加锁:
SELECT ... FOR UPDATE
- 提升隔离级别:
SERIALIZABLE
20. MySQL三大日志
解答
日志 | 作用 | 层级 |
---|---|---|
Binlog | 主从复制、数据恢复(逻辑日志) | MySQL Server层 |
Redo Log | 崩溃恢复(物理日志) | InnoDB引擎层 |
Undo Log | 事务回滚、MVCC(逻辑日志) | InnoDB引擎层 |
协同机制:两阶段提交保证Binlog与Redo Log一致性 |
21. 说一下Redo Log
解答
作用:确保事务持久性,宕机后恢复未刷盘数据
流程:
- 事务修改数据页 → 写入内存Buffer Pool
- 生成Redo Log记录 → 写入Log Buffer
- 事务提交时Log Buffer刷盘(策略由
innodb_flush_log_at_trx_commit
控制)
特点:
- 顺序写(比随机写快10倍+)
- 循环写入文件组(
ib_logfile0/1
)
22. 你知道什么是事务的两阶段提交吗
解答
目的:保证Redo Log与Binlog逻辑一致性
阶段:
Prepare
:
- 写Redo Log并标记
PREPARE
- 写Binlog到内存
Commit
:
-
Binlog刷盘
-
Redo Log标记
COMMIT
崩溃恢复逻辑: -
Binlog完整 → 提交事务
-
Binlog不完整 → 回滚事务
23. 说一下Redis的持久化几种
解答
方式 | 原理 | 优点 | 缺点 |
---|---|---|---|
RDB | 定时内存快照(SAVE /BGSAVE ) | 恢复快、文件小 | 可能丢失最后一次快照数据 |
AOF | 追加写操作命令 | 数据更安全 | 文件大、恢复慢 |
混合 | RDB+AOF(Redis 4.0+) | 速度与安全兼顾 | 复杂度高 |
生产建议:启用RDB+AOF 混合模式 |
24. 说一下RDB和AOF的刷盘策略
解答
RDB触发条件:
- 手动触发:
SAVE
(阻塞)/BGSAVE
(后台fork) - 自动触发:
save <seconds> <changes>
配置阈值
AOF刷盘策略: always
:每条命令刷盘(安全,性能差)everysec
:每秒刷盘(平衡,默认)no
:由OS决定(性能好,可能丢数据)
优化:定期执行BGREWRITEAOF
压缩AOF文件
25. 说一下分布式锁的实现
解答
方案 | 原理 | 缺点 |
---|---|---|
数据库唯一索引 | 利用唯一键冲突插入锁记录 | 性能低、无自动失效机制 |
Redis SETNX | SET key value NX EX + 过期时间 | 主从切换可能导致锁失效 |
ZooKeeper | 创建临时有序节点,最小节点获锁 | 性能较低 |
RedLock | 多Redis实例(N/2+1成功即获锁) | 实现复杂 |
推荐方案:Redis单实例或Redisson框架 |
26. Redisson怎么实现分布式锁
解答
核心流程:
加锁
:
- Lua脚本原子操作:
SET lock_key uuid NX PX expire
- 启动看门狗线程自动续期(默认每10秒续约)
解锁
:
-
Lua脚本校验锁归属(UUID匹配)后删除
-
发布解锁消息唤醒等待线程
特性: -
可重入:通过计数器实现
-
公平锁:基于ZooKeeper顺序节点
-
红锁(RedLock):多实例容错
27. 说一下MySQL和Redis的数据一致性问题
解答
常见方案对比:
策略 | 操作顺序 | 问题 |
---|---|---|
先更DB后删缓存 | 更新DB → 删除缓存 | 删缓存失败导致脏读 |
先删缓存后更DB | 删除缓存 → 更新DB | 更新DB失败时缓存已删 |
优化方案: |
- 延时双删:
删缓存 → 更新DB → 休眠 → 再删缓存 - Binlog监听:
Canal监听Binlog → 更新/删除缓存(最终一致) - 设置缓存过期:
容忍短期不一致,依赖过期时间兜底
关键原则:强一致性难实现,优先保证DB正确性