jstack可直接查看线程状态,关键在于理解runnable、blocked、waiting、timed_waiting等状态含义及对应锁信息:blocked需关注“waiting to lock”地址,waiting/timed_waiting要结合栈顶方法判断合理性,定位异常须用grep筛选关键词并配合-l参数分析锁依赖链。

直接运行 jstack
怎么看线程状态字段
jstack 输出中每个线程块都包含一行 java.lang.Thread.State:,后面跟着具体状态值。这是 JVM 官方定义的六种状态之一:
- RUNNABLE:线程正在运行或就绪,可能在执行 Java 代码,也可能在等待 CPU;若持续大量线程卡在这里且 CPU 高,要查是否有死循环或密集计算
- BLOCKED:正等待进入 synchronized 同步块/方法,输出里会明确写 “waiting to lock ”,说明它被另一把锁拦住了
- WAITING:无限期等待,常见于 Object.wait()、join()、LockSupport.park();需结合栈顶方法判断是否合理(如线程池空闲线程在此状态属正常)
- TIMED_WAITING:有超时的等待,比如 sleep(1000)、wait(5000)、Thread.join(3000);如果超时时间很长且反复出现,可能是下游响应慢或逻辑卡点
- TERMINATED:已结束,不会出现在活跃 dump 中
- NEW:刚 new 出来还没 start,dump 里基本看不到
怎么快速定位可疑线程
别从头翻完整日志,用关键词精准筛选:
- 查阻塞竞争:grep "BLOCKED" thread_dump.log,再看 “waiting to lock” 后面的地址,多个线程等同一把锁就是热点
- 查死锁线索:jstack -l
| grep -A 10 "Found one Java-level deadlock" ,JVM 自动检测到才会输出,不是每次都有 - 查长时间挂起:grep "WAITING\|TIMED_WAITING" thread_dump.log | grep -E "(at com\.yourpackage|lock\(|tryLock|acquire)",聚焦业务代码里的锁调用点
- 查高 CPU 线程:top -H -p
找出 nid(十六进制),再在 jstack 输出里搜 nid=0x... 对应的线程栈
锁信息怎么对应着看
加 -l 参数后,jstack 会显示更详细的锁持有关系:
- locked 表示该线程已拿到这把锁
- waiting to lock 表示它正排队等这把锁
- 同一个地址出现在不同线程的 locked / waiting to lock 中,就构成锁依赖链,是分析死锁的基础
- 注意区分:synchronized 锁看 object monitor 地址;ReentrantLock 等显式锁要看 java.util.concurrent.locks 下的具体实例哈希
容器环境和特殊场景要注意什么
在 Docker/K8s 里跑 Java 应用时,jstack 可能报错或不全:
- 提示 “Unable to get pid of LinuxThreads manager thread”:大概率是容器没挂载 /proc 或权限不足,尝试加 -F 强制 dump(需 root)
- 看不到虚拟线程(Virtual Threads):JDK 21+ 默认不显示,得加 --show-virtual-threads(或对应版本参数)
- “Waiting on condition” 不等于问题:90% 是正常挂起(比如 Netty EventLoop 等 IO),重点看栈顶是不是业务锁方法,而不是光看状态名
- 分布式锁(Redis/ZK)jstack 看不到持有关系:它只管 JVM 内部锁,外部协调服务的问题得靠日志+监控交叉验证
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











