死锁与锁竞争排查:jstack / Arthas、锁顺序与典型案例

发布时间:2026/6/14 8:17:31

死锁与锁竞争排查:jstack / Arthas、锁顺序与典型案例 线上最“要命”的并发问题通常是两类死锁Deadlock线程互相等待系统直接卡死热点锁竞争Lock Contention系统还能跑但 RT 抖动、吞吐下降这篇给你一条可落地的排查路线先判断是死锁还是竞争用 jstack 快速定位“谁拿着锁、谁在等锁”需要更细时用 Arthas 做在线定位最后回到工程修复锁顺序、锁粒度、锁分离1. 先定性死锁 vs 锁竞争1.1 死锁的特征线程状态通常稳定没有进展CPU 可能不高但业务不动jstack往往直接提示Found one Java-level deadlock1.2 锁竞争的特征仍然有进展但延迟抖动线程大量BLOCKED或WAITING (parking)RT 的 P99/P999 上升2. 最常用工具jstack抓一次不够抓三次2.1 抓栈原则连续抓 3 次间隔 5~10 秒对比线程栈是否“固定不动”2.2 看懂两类关键信息对于 synchronizedmonitor- waiting to lock 0x... (a Xxx)在等锁- locked 0x... (a Xxx)持有锁对于 ReentrantLock / AQS关键词常见为java.util.concurrent.locks.LockSupport.parkAbstractQueuedSynchronizer.acquireConditionObject.await3. 死锁经典案例锁顺序不一致3.1 复现代码finalObjectlockAnewObject();finalObjectlockBnewObject();Threadt1newThread(()-{synchronized(lockA){sleep(100);synchronized(lockB){// ...}}});Threadt2newThread(()-{synchronized(lockB){sleep(100);synchronized(lockA){// ...}}});本质t1 拿 A 等 Bt2 拿 B 等 A3.2 修复原则统一锁顺序所有地方都 A-B或者把两个锁合并为一个牺牲并发换安全4. 热点锁竞争怎么定位“是哪把锁”4.1 先看线程状态BLOCKED多为 monitor 竞争WAITING (parking)多为 AQS/Lock 竞争4.2 从栈回到业务方法你要找到这两点线程都卡在哪个方法方法里锁住了什么资源对象锁/类锁/某个 map/某个队列常见“热点锁”来源大对象上的 synchronized静态 synchronized类锁全局单例锁把 IO/RPC 放进临界区5. Arthas 在线定位适合“能跑但慢”如果你已经能连到线上机器Arthas 常用命令thread -b查看阻塞线程thread id看具体线程栈stack/trace定位热点方法谨慎用注意开销实践建议先用thread -b找到阻塞/等待最多的线程再定位它们共同卡住的业务方法6. 工程修复手段按优先级缩小临界区只锁共享变量更新不要锁 IO/RPC锁分离把一个大锁拆成多个小锁按 key 分段读写分离读多写少用读写锁或 CopyOnWrite避免嵌套锁嵌套锁是死锁温床尝试超时锁用tryLock(timeout)避免无限等待7. 面试追问 QA高频Q为什么抓三次 jstackA一次可能是瞬态三次能判断是否长期卡住。QBLOCKED 和 WAITING(park) 区别ABLOCKED 多是 monitorWAITING(park) 多是 AQS/Lock。Q怎么预防死锁A统一锁顺序、减少嵌套锁、必要时加超时获取锁。8. 面试表达30 秒讲清楚我先区分死锁还是锁竞争死锁通常 jstack 会直接提示并且栈固定。用 jstack 看locked/waiting to lock或 AQS 的park/acquire定位是哪把锁和哪个业务方法。热点锁就缩小临界区、拆锁/分段、把 IO/RPC 移出锁。死锁主要靠统一锁顺序、减少嵌套锁、必要时用 tryLock 超时。9. 总结定性死锁/竞争比“调参数”更重要jstack 是第一现场抓三次对比最有效解决问题回到工程锁粒度、锁顺序、锁分离

相关新闻