4. 并发编程与锁优化

并发编程是分布式系统高并发场景的基石,锁优化直接决定系统吞吐与稳定性。本章覆盖 synchronized、ReentrantLock、AQS、线程池、CAS 五大核心,从锁机制原理到线程池调优,再到分布式锁实战,构建完整并发知识体系。

核心概念一句话定位面试权重
synchronizedJVM 内置锁,自动加锁/释放,支持锁升级⭐⭐⭐⭐⭐
ReentrantLockAPI 级别可重入锁,支持公平/非公平、中断、Condition⭐⭐⭐⭐⭐
AQS抽象队列同步器,CLH 变体 + state + CAS,模板方法模式⭐⭐⭐⭐⭐
线程池管理线程生命周期,七大参数 + 四步执行流程⭐⭐⭐⭐⭐
CAS乐观锁核心,硬件级原子操作,存在 ABA 问题⭐⭐⭐⭐
锁优化面试回答框架 ① 识别竞态条件 分析共享资源访问路径 ② 选择锁类型 synchronized / Lock / CAS ③ 细化锁粒度 分段锁 / 读写锁 / 无锁 ④ 优化锁性能 锁消除 / 锁粗化 / 降级 ⑤ 验证正确性 压测 + 代码审查 💡 面试时按此路径组织回答,体现「分析 → 选型 → 优化 → 验证」的完整思维链条

Q1:synchronized 和 ReentrantLock 有什么区别?实际开发中如何选择?

🎯 考察点 对两种锁机制的理解深度,是否了解底层实现差异及适用场景

回答框架:从 语法 → 功能特性 → 底层实现 → 性能 → 选型建议 五个维度对比。

维度synchronizedReentrantLock
语法JVM 关键字,自动加锁/释放API 接口,lock/unlock 配对
公平性非公平支持公平/非公平
中断响应不支持支持 lockInterruptibly
条件变量wait/notify 单一条件多个 Condition 对象
底层实现锁升级(偏向→轻量→重量)AQS + CAS + LockSupport
性能(JDK 8+)优化后与 Lock 接近高并发下略优

📝 示例回答:synchronized 是 JVM 内置锁,自动释放,支持锁升级(偏向锁→轻量锁→重量锁);ReentrantLock 是 API 级锁,需手动释放,提供可中断、公平锁、多 Condition 等高级功能。简单场景选 synchronized,需要超时中断或公平锁时选 ReentrantLock。

🔍 可能追问:

  • synchronized 的锁升级过程具体是怎样的?偏向锁是否一定比轻量锁快?
  • ReentrantLock 的公平锁与非公平锁在性能上有什么区别?
  • JDK 6 之后 synchronized 做了哪些优化?
⚠️ 避坑提醒:不要再说「synchronized 性能差」,JDK 6 引入锁升级和自适应自旋后,单机场景下性能已与 ReentrantLock 持平。也不要把「ReentrantLock 一定比 synchronized 快」作为结论。

Q2:请讲一下 AQS 的实现原理,如何基于 AQS 实现一个自定义同步器?

🎯 考察点 对 AQS 核心机制的理解,是否具备模板方法模式的工程应用能力

回答框架:核心组件(state + CLH 队列 + CAS)→ 模板方法模式 → 自定义实现步骤 → 代码示例。

  1. state:volatile 修饰的同步状态,通过 CAS 更新
  2. CLH 队列:双向链表,线程获取锁失败时入队等待
  3. 模板方法:acquire / release 定义骨架,tryAcquire / tryRelease 由子类实现
  4. 自定义步骤:继承 AbstractQueuedSynchronizer,重写 tryAcquire / tryRelease(独占)或 tryAcquireShared / tryReleaseShared(共享)

📝 示例回答:AQS 基于 volatile state 和 CLH 变体队列实现,通过 CAS 更新 state,失败则入队等待。自定义同步器需重写 tryAcquire/tryRelease(独占模式)或 tryAcquireShared/tryReleaseShared(共享模式)。例如实现一个互斥锁,只需将 state 0→1 视为获取锁,1→0 视为释放锁。

🔍 可能追问:

  • AQS 如何解决线程阻塞与唤醒的性能问题?为什么要用 CLH 队列而不是其他队列?
  • 共享模式(如 CountDownLatch、Semaphore)与独占模式(如 ReentrantLock)的区别是什么?
  • AQS 中的 Node 节点状态(CANCELLED / SIGNAL / CONDITION)分别代表什么?
⚠️ 避坑提醒:不要忽略 CLH 队列是「变体」而非原始 CLH,AQS 引入了前驱节点唤醒和后驱节点检查机制。回答时建议区分「独占/共享」两种模式,避免只讲独占。

Q3:线程池的核心参数有哪些?提交一个任务后线程池的执行流程是什么?如何合理配置线程数?

🎯 考察点 对线程池机制的理解,是否能结合业务场景合理配置参数

回答框架:七大参数 → 四步执行流程 → 配置策略 → 监控与调优。

七大参数速记:corePoolSize · maxPoolSize · keepAliveTime · unit · workQueue · threadFactory · rejectHandler

执行流程:

  • ① 核心线程数未满 → 创建核心线程执行
  • ② 核心线程已满 → 任务入阻塞队列
  • ③ 队列已满 → 创建非核心线程(不超过 maxPoolSize)
  • ④ 线程数已达 maxPoolSize → 执行拒绝策略

配置策略:CPU 密集型 → N+1 | IO 密集型 → 2N+1 | 混合型 → 拆分后分别配置

📝 示例回答:七大参数:corePoolSize、maxPoolSize、keepAliveTime、unit、workQueue、threadFactory、rejectHandler。流程:先 corePoolSize,再入队列,再到 maxPoolSize,最后拒绝策略。CPU 密集型配置 N+1,IO 密集型配置 2N+1(N 为 CPU 核数),避免过度创建线程导致上下文频繁切换。

🔍 可能追问:

  • 拒绝策略有哪些?AbortPolicy / CallerRunsPolicy / DiscardPolicy / DiscardOldestPolicy 分别适用什么场景?
  • 如何动态调整线程池参数?使用 ThreadPoolExecutor 的 setCorePoolSize / setMaximumPoolSize 方法。
  • 线程池的监控指标有哪些?activeCount / completedTaskCount / largestPoolSize / queue.size 等。
⚠️ 避坑提醒:不要忽略 workQueue 的容量限制,无界队列(如 LinkedBlockingQueue 默认 Integer.MAX_VALUE)会导致 maxPoolSize 失效。生产环境建议使用有界队列 + 自定义拒绝策略。

📌 项目案例:高并发秒杀系统的锁优化实践

项目名称 电商秒杀活动订单系统
业务背景 大促期间库存扣减与订单创建存在严重锁竞争,接口 TP99 超过 2s,导致用户体验差、订单流失率高
规模指标 峰值 QPS 5000+,日均订单 800 万,库存 SKU 数量 10 万+,单 SKU 库存竞争激烈
技术栈 Spring Boot · Redis Cluster · MySQL · RabbitMQ · 本地缓存 Caffeine

核心难点与解决方案

核心难点解决方案效果
单 SKU 库存扣减并发冲突 Redis 分段锁 + 本地缓存预热,将单一 key 拆分为 8 个 segment 锁冲突降低 85%
数据库行锁竞争 异步批量写入 + 最终一致性补偿,下单时只写 Redis,MQ 异步落库 DB 锁等待下降 92%
热点 SKU 导致的线程池阻塞 独立线程池隔离热点 SKU 处理,拒绝策略降级为快速失败并返回提示 整体吞吐提升 3 倍

面试高频追问

  • 为什么选择 Redis 分段锁而不是 Redlock?—— 分段锁降低单 key 压力,Redlock 偏重跨节点一致性,秒杀场景可接受短暂超卖。
  • 分段锁的 segment 数量如何确定?—— 根据 SKU 热度动态调整,热 SKU 分段多,冷 SKU 分段少,避免资源浪费。
  • 本地缓存与 Redis 如何保证一致性?—— 采用「先写 Redis,再异步失效本地缓存」策略,配合版本号校验,容忍秒级不一致。
  • 如果 Redis 宕机如何兜底?—— 本地熔断降级,直接读取数据库库存并加分布式写锁,保证系统不崩溃。

可迁移回答话术

“在秒杀场景中,我们通过 分段锁 + 本地缓存预热 降低锁粒度,将单 SKU 的库存拆分为 8 个 Redis segment,每个 segment 独立加锁,锁冲突率降低了 85%。同时用 Caffeine 本地缓存 承载 90% 的读请求,Redis 只处理写请求和缓存同步,最终 TP99 从 2s 降至 200ms。这套方案的核心思路是 『读写分离 + 分段隔离』,可迁移到任何高并发库存/余额类场景。”

💡 迁移提示:面试其他项目时,只要涉及「高并发写 + 热点资源竞争」,都可以复用「分段+本地缓存+异步落库」的话术结构,重点突出「锁粒度量化对比」和「TP99 改善幅度」两个关键数据。

📖 本章核心脉络

  • 锁机制选型: synchronized 适用于简单同步,ReentrantLock 适用于高级并发场景(公平、中断、多条件)
  • AQS 原理: 理解 state + CLH 队列 + 模板方法,是自定义同步器的基础
  • 线程池调优: 七大参数 + 四步流程 + 拒绝策略,结合业务类型配置线程数
  • 锁优化实战: 分段锁降低粒度、锁消除/锁粗化、CAS 无锁化、读写分离
  • 分布式锁: Redis 分段锁 + 本地缓存 + 异步落库,兼顾性能与一致性