应先用jstack -l <pid>获取线程快照,检查BLOCKED/WAITING/RUNNABLE状态及末尾“Found one Java-level deadlock”提示,再结合CPU、线程数、GC等监控指标分场景定位IO阻塞、死锁、活锁或线程池问题。

程序卡死却不报错、不崩溃、日志停更,这种“静默假死”在 Java 多线程场景中很典型。核心思路不是猜,而是分层验证:先确认线程是否真卡住,再定位卡在哪一环,最后针对性修复。
看线程快照,确认真实状态
用 jstack 获取当前所有线程的调用栈和锁信息,这是最直接的“线程 CT 片”:
- 执行
jstack -l <pid> > threaddump.log(Linux/macOS),或 Windows 下按Ctrl+Break - 重点检查三类状态:BLOCKED(等锁)、WAITING / TIMED_WAITING(等 notify、join、sleep 或 IO 返回)、RUNNABLE 但 CPU 占用极高(可能是死循环或活锁)
- 留意 jstack 输出末尾是否有 Found one Java-level deadlock —— 这是 JVM 自动检测到的死锁,不用人工分析循环等待链
查阻塞根源,分场景处理
不同卡死现象对应不同底层原因,不能一概而论:
- 大量线程 BLOCKED 在同一把锁上:说明存在锁竞争瓶颈。检查是否用了全局 synchronized 方法、未拆分的共享资源锁,或锁粒度太大
- 线程停在 SocketInputStream.socketRead0、MysqlIO.readPacket 等 IO 调用:大概率是数据库/HTTP/Redis 调用没设超时,外部依赖无响应导致线程永久挂起
- 线程池队列持续积压,活跃线程数 = 核心数,submit() 调用卡住:说明任务提交被拒绝策略吞掉异常,或线程池配置不合理(如队列无限大 + 拒绝策略为 AbortPolicy)
- 多个线程处于 RUNNABLE,CPU 飙升但业务无进展:可能是活锁(如重试逻辑没退避)、自旋等待条件不满足,或 GC 线程频繁抢占(配合 jstat -gc 实时观察)
结合监控交叉验证
单靠线程快照容易误判,需搭配运行时指标缩小范围:
立即学习“Java免费学习笔记(深入)”;
- CPU 持续接近 100% → 查 RUNNABLE 线程堆栈,重点关注循环体、正则匹配、JSON 序列化等高开销操作
- CPU 接近 0%,线程全在 WAITING/BLOCKED → 优先排查死锁、IO 阻塞、未唤醒的 wait()、线程池耗尽
- 线程数缓慢上涨不释放 → 可能是线程泄漏(比如 ThreadLocal 未 remove、异步任务未关闭 ExecutorService)
- Full GC 频繁且耗时长 → 堆内存不足或对象生命周期设计不合理,间接导致线程因 GC Stop-The-World 而“卡顿”
日常预防比事后救火更重要
卡死问题往往暴露的是架构或编码习惯的隐患:
- 所有网络/数据库调用必须显式设置 connectTimeout 和 readTimeout,绝不依赖默认值
- 多锁场景强制统一加锁顺序(如按类名/资源 ID 字典序),避免交叉获取
- 线程池不用无界队列(LinkedBlockingQueue 不设容量),改用有界队列 + CallerRunsPolicy 或自定义拒绝逻辑
- 关键路径禁用 wait()/notify(),优先用 CountDownLatch、CyclicBarrier、CompletableFuture 等更安全的同步工具


















