真实面经题目 · 原创解析

并发怎么控制?

并发控制的核心不是简单加锁,而是在正确性、吞吐量、延迟和资源保护之间做取舍。高质量回答应先说明控制目标,再识别共享资源和一致性边界,最后按单机内存、线程调度、数据库、分布式协调、入口限流和异步削峰等层次选择手段。

出现于:阿里巴巴 · 测开

60 秒回答模板

并发控制可以从三层回答。第一层是目标:避免数据竞争、重复执行、超卖、脏写和资源打爆,同时控制延迟和错误率。第二层是对象:识别共享资源,例如内存变量、本地缓存、集合、文件句柄、连接池、库存行、订单状态、账户余额、接口调用次数。第三层是方案:单 JVM 内共享状态用 synchronized、ReentrantLock、读写锁、CAS、原子类、并发容器;任务执行用线程池、有界队列、背压和超时;入口保护用限流、熔断、降级;重复请求用幂等键、唯一索引、状态机;数据库并发用乐观锁、悲观锁、事务隔离和行锁;跨节点互斥用分布式锁,但要关注锁过期、续期、误删和数据库兜底;突发流量用消息队列削峰,但消费端必须幂等并处理重试、死信和积压。

考点 先明确控制目标
主线 识别共享资源
易错点 只回答 synchronized,没有说明并发控制的目…

深入解析

01

先明确控制目标

并发控制要同时解决正确性和稳定性。正确性包括避免竞态条件、丢失更新、重复扣减、重复创建、脏写和状态覆盖;稳定性包括避免线程数、连接数、队列长度、数据库锁等待、下游接口调用失控。面试中不要只说线程安全,还要说明吞吐量、延迟、资源隔离、可恢复性和可观测性。

02

识别共享资源

判断是否需要并发控制,要先找共享资源和临界区。共享资源可能是 Java 对象字段、静态变量、本地缓存、集合、文件句柄、连接池、库存行、订单状态、账户余额、幂等记录、下游接口额度。临界区越小越好,只保护真正需要原子性的读改写过程;如果能线程内私有、请求内私有、不可变对象化或按 key 分片,就优先减少共享。

03

单机互斥

单 JVM 内最直接的方式是互斥锁。synchronized 适合简单临界区,语义清晰,由 JVM 管理锁释放;ReentrantLock 适合需要可中断、可超时、公平锁或多个 Condition 的场景;读写锁适合读多写少,允许多个读线程并发但写线程独占。风险是锁粒度过大、锁顺序不一致导致死锁,以及锁内执行 IO 或远程调用导致长时间阻塞。

04

CAS 与原子类

CAS 和 AtomicInteger、AtomicLong、AtomicReference、LongAdder 等适合简单计数、状态切换和无锁更新。它们通过比较并交换减少阻塞,适合低冲突或简单状态场景。高冲突下 CAS 可能自旋消耗 CPU;涉及多个变量一致性时,单个原子类不够,需要封装状态、加锁或使用事务。LongAdder 适合高并发统计,但不适合强一致库存扣减。

05

线程池与背压

线程池控制的是并发执行数量,不是数据一致性本身。核心参数包括核心线程数、最大线程数、队列容量、拒绝策略、线程命名、超时和监控。CPU 密集任务线程数不宜过多,IO 密集任务可适当增加但要受下游容量约束。阻塞队列应有界,背压要求消费者处理不过来时让上游减速、失败或降级,而不是无限堆积请求。

06

业务幂等

重复请求、超时重试和消息重复投递都可能造成重复执行。幂等可以用请求唯一号、业务唯一键、数据库唯一索引、状态机流转、去重表、缓存短期去重等实现。幂等不是简单先查再插,因为并发下 check-then-act 本身有竞态;可靠做法通常依赖唯一约束、原子插入、事务或状态条件更新。

07

数据库并发控制

数据库层常用乐观锁和悲观锁。乐观锁通过 version 字段或条件更新实现,适合冲突不高、重试成本可控的场景;悲观锁通过 select for update 或行锁提前锁住数据,适合强一致且冲突较高的关键资源,但会增加锁等待和死锁风险。事务隔离级别、索引设计和锁范围也会影响并发行为。

08

分布式锁与队列

多实例部署时,JVM 锁只能保护单进程,跨节点互斥需要 Redis、ZooKeeper 或数据库锁。分布式锁要考虑唯一锁标识、过期时间、释放原子性、续期、可重入、业务执行超过租期和锁服务异常。消息队列可把突发同步流量转为可控异步消费,但会引入最终一致性、重复消费、乱序、积压和死信处理问题。

易错点

  • 只回答 synchronized,没有说明并发控制的目标、共享资源和场景选择。
  • 把线程池当成线程安全手段,忽略线程池只能限制执行并发。
  • 认为加了 Redis 分布式锁就绝对安全,忽略锁过期、误删、续期和数据库兜底。
  • 用先查询再插入实现幂等,忽略并发下两个请求可能同时通过检查。
  • 锁粒度过大,把远程调用或慢 IO 放进锁内,导致吞吐下降和锁等待扩大。
  • 滥用无界队列,短期看没有拒绝,长期会造成内存压力、延迟堆积和故障放大。

面试官追问

synchronized 和 ReentrantLock 怎么选?

简单临界区优先 synchronized,语义清楚,异常时自动释放。需要 tryLock、超时等待、可中断等待、公平锁或多个 Condition 时选择 ReentrantLock。两者都要避免锁内执行慢 IO 和远程调用。

CAS 一定比锁好吗?

不一定。CAS 适合简单变量、低冲突、短逻辑更新。高冲突下大量自旋会浪费 CPU;多个变量需要共同保持一致时,CAS 复杂度会上升,锁或事务反而更清晰。

如何防止库存超卖?

核心是让扣减动作在数据库层具备原子条件,例如库存大于零时才更新,或使用 version 乐观锁。高并发入口可以加限流、队列削峰、分布式锁或分段库存,但数据库条件更新仍应兜底。

接口被重复提交怎么控制?

使用幂等键或业务唯一键,把请求唯一标识落到数据库或缓存中。强一致场景优先依赖数据库唯一索引或状态机条件更新,避免先查再插造成并发竞态。

线程池参数怎么考虑?

先区分 CPU 密集还是 IO 密集,再看下游容量。CPU 密集线程数接近 CPU 核数;IO 密集可以更高,但要受连接池、数据库和外部接口承载能力限制。队列应有界,拒绝策略要明确,关键指标要监控。