线程因锁竞争阻塞的本质是多线程争抢同一把锁导致排队等待,表现为接口变慢、吞吐量低、cpu不高但延迟飙升;可通过jstack定位blocked线程集中点,优化方向包括缩小锁粒度、换用读写锁或无锁结构,并通过线程数、吞吐量和p99延迟验证效果。

线程因锁竞争进入阻塞状态,本质是多个线程争抢同一把锁导致排队等待。这类问题不会立刻报错,但会表现为接口变慢、吞吐量上不去、CPU不高却响应延迟飙升——它藏得深,影响却很实。
看线程堆栈定位阻塞点
用 jstack
- 大量线程处于 BLOCKED 状态,且堆栈都停在同一个
synchronized方法或ReentrantLock.lock()调用处 - 少数线程处于 RUNNABLE 状态,正执行该同步块内的逻辑(比如数据库操作、缓存刷新),说明它正持有锁
例如:100个线程都卡在 OrderNoGenerator.next() 的 synchronized(this) 上,而只有一个线程正在里面查数据库——这就是典型的单点锁瓶颈。
缩小锁粒度,避免全局串行化
锁范围越大,竞争越激烈。常见优化方向:
- 把类锁(
synchronized static)改成实例锁,让不同对象各自持锁 - 把大同步块拆成小段,只对真正共享修改的代码加锁,读操作尽量不锁
- 用分段锁替代全局锁,比如
ConcurrentHashMap内部把数据分成16段,最多支持16个线程并发写 - 对计数器类场景,优先用
AtomicInteger或LongAdder,它们通过CAS+分槽机制避开锁竞争
换更合适的锁策略
不是所有场景都适合 synchronized 或独占锁:
- 读多写少(如配置管理、元数据缓存):改用
ReentrantReadWriteLock,允许多个线程同时读,仅写时互斥 - 需要超时控制或可中断等待:用
ReentrantLock的tryLock(long, TimeUnit),避免无限等待 - 热点对象频繁更新:考虑无锁设计,比如基于消息队列异步化,或用环形缓冲区(Disruptor)批量处理
验证优化是否生效
改完不能只看“不报错”,要盯住三个指标:
- 线程 dump 中 BLOCKED 线程数量明显下降
- 压测时吞吐量随并发线程数上升而稳定增长(不再出现“开更多线程反而更慢”的拐点)
- P99 响应时间毛刺减少,长尾延迟收敛
锁竞争不是玄学,它是可观测、可量化、可优化的具体瓶颈。关键在于别把它当成“线程太多”的锅,而是回到代码里,看清谁在抢、抢什么、为什么非抢不可。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











