高并发问题与排查
一、常见高并发底层问题
1. 锁竞争 & 上下文切换
(1)锁竞争
- 现象:大量线程争抢同一把锁,大量
BLOCKED线程,请求排队、接口 rt 飙升、吞吐量下降。 - 原因:
synchronized/ReentrantLock锁粒度过大- 共享变量无脑加锁,串行化严重
- 全局唯一大锁,并发退化串行
- 后果:锁等待耗时、服务吞吐上不去、高峰期雪崩。
(2)上下文切换
CPU 线程来回切换执行:
- 线程过多、频繁阻塞 / 唤醒、竞争激烈,都会触发频繁上下文切换
- 每次切换:保存现场 → 恢复现场,消耗 CPU 资源
- 现象:CPU 高负载但业务代码不复杂,系统整体变慢
2. 伪共享(False Sharing)
原理
CPU 缓存以 CacheLine(缓存行,64 字节) 为最小单位。
- 多个独立变量落在同一个缓存行
- 线程 A 修改变量 A,会导致同一行内变量 B 缓存失效
- 线程 B 修改变量 B 也要重新刷缓存
- 互不相关的变量互相干扰,频繁缓存失效,性能暴跌
场景
并发框架、计数器、批量统计、多线程高频读写独立变量。
解决
- 缓存行填充:
@sun.misc.Contended注解(JDK1.8+) - 拆分对象、避免高频读写字段紧邻
3. GC 压力过大
高并发下高频问题:
- 瞬时流量大,创建大量短生命周期对象 → YoungGC 频繁
- 本地缓存、大集合、线程池堆积 → 对象老化,FullGC 频繁
- 并发容器 / 异步任务堆积 → 内存持续上涨,最终 OOM
- GC 停顿时间变长,接口抖动、超时
高并发 GC 典型诱因
- 大量临时字符串、DTO、循环内创建对象
- 无界队列、无限积压任务
- ThreadLocal 不回收导致内存泄漏
二、线上排查工具 & 实战用法
1. jstack (最常用)
作用:查看线程快照、定位死锁、阻塞、死循环
1 | |
能排查什么
- 死锁:搜索
Found one Java-level deadlock - 线程阻塞:大量
BLOCKED等待锁 - 线程卡死:
WAITING / TIMED_WAITING堆积 - 无限循环:RUNNABLE 一直占用 CPU
2. jconsole
可视化 JDK 自带工具:
- 线程状态监控、死锁检测
- 堆内存、线程数、GC 趋势
- 适合简单快速排查、本地 / 测试环境
3. Arthas(线上最强排查)
核心常用命令:
thread:查看全部线程、CPU 占比、阻塞线程、死锁trace:追踪接口耗时,定位慢方法watch:监听方法入参 / 出参 / 异常gc:实时查看 GC 频率、堆内存dashboard:全局 CPU、内存、线程、负载面板
高并发必备:不用重启、不侵入代码,线上快速定位:
- 锁等待、慢接口、死循环、内存泄漏、线程堆积
三、典型故障定位
1. 死锁
- 现象:接口长期卡住、无报错、CPU 不高、线程大量 BLOCKED
- 排查:
jstack/ arthasthread直接检测循环锁依赖 - 根源:互相持有对方锁、循环等待
2. 线程阻塞(非死锁)
- 等待外部接口、数据库、redis、锁、Condition、park
- 大量
WAITING线程堆积 - 线程池队列爆满、拒绝策略触发
3. CPU 飙高
- 死循环、复杂计算、频繁序列化、频繁 GC
- 用
arthas thread -n 5看 TOP CPU 线程
四、高并发性能优化核心思路
1. 减小锁粒度、降低锁竞争
- 拆分大锁 → 细粒度锁
- 读写分离:
ReentrantReadWriteLock - 无锁优化:CAS、ThreadLocal、局部变量
- 杜绝锁嵌套、缩短锁内代码执行时间
2. 合理线程池设计
- 区分 IO 密集 / CPU 密集 配置线程数
- 全部使用有界队列,限制最大线程,防 OOM
- 自定义拒绝策略:告警 + 降级 + 任务落地
- 异步任务指定独立线程池,避免公共池互相影响
3. 减少上下文切换
- 控制线程总数,不要无限创建线程
- 避免频繁短等待、频繁唤醒 / 阻塞
- 业务合并、批量处理,减少线程交互
4. 内存 & GC 优化
- 复用对象、池化思想(连接池、对象池)
- 避免循环创建临时对象
- 合理设置 JVM 参数:新生代大小、GC 收集器 (G1/ZGC)
- 定时清理本地缓存、ThreadLocal.remove ()
5. 并发容器替代普通容器
ConcurrentHashMap替代 HashMapCopyOnWriteArrayList读多写少场景- 阻塞队列解耦生产者消费者,削峰填谷
6. 异步 + 削峰 + 缓存
- 核心接口多级缓存(本地缓存 + 分布式缓存)
- 非核心逻辑异步化(CompletableFuture、MQ)
- 限流、熔断、降级,保护核心服务
- 批量聚合、合并请求,降低 QPS 压力
7. 规避底层坑
- 解决伪共享、缓存行竞争
- 避免大事务、长连接阻塞
- 数据库 / 中间件连接池合理配置,防止连接耗尽
极简背诵总结
锁竞争:锁粒度过大、全局大锁 → 接口阻塞、吞吐低;
上下文切换:线程过多、频繁阻塞唤醒 → CPU 损耗。
伪共享:多变量共用缓存行,互相缓存失效;用缓存行填充解决。
GC 压力:短对象泛滥、无界积压、内存泄漏 → 频繁 GC、OOM。
排查工具:
- jstack:线程快照、死锁;
- jconsole:简易可视化监控;
- Arthas:线上全能排查,定位慢方法、阻塞、CPU。
优化方向:
减小锁粒度 + 合理线程池 + 异步削峰 + 缓存 + GC 优化 + 并发容器 + 限流降级。
高并发问题与排查
http://hanqichuan.com/2026/04/16/java并发/高并发问题与排查/