线程池本身不会死锁,但工作线程可能因加锁顺序不当、资源竞争或跨层阻塞而全部卡在WAITING或BLOCKED状态,导致任务堆积、接口超时、日志停滞、CPU偏低。

线程池本身不会死锁,但其中的工作线程可能因业务代码加锁顺序不当、资源竞争或跨层阻塞(如数据库行锁 + 应用锁),导致全部卡在 WAITING 或 BLOCKED 状态,表现为任务堆积、接口超时、日志停滞、CPU偏低。
看现象:先确认是不是真卡住
别急着 dump,先快速验证典型特征:
- 线程池活跃线程数长期满载(例如 core=10、max=20,当前 active=20 且长时间不变)
- HTTP 健康检查(如
/actuator/health)持续超时或返回 503 - 应用日志停止刷新,或只重复打印相同 warn/error(无新业务 traceId)
- jconsole / VisualVM 中看到大量线程状态为 WAITING(等待 notify/await)或 BLOCKED(等待 monitor enter)
- CPU 使用率明显偏低(
抓快照:用 jstack -l 获取带锁详情
必须加 -l 参数,否则 ReentrantLock、Condition 等显式锁信息会丢失:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用启动应用的同一用户执行:
jstack -l <pid> > thread_dump.log - 生产环境建议加时间戳:
jstack -l <pid> > jstack_$(date +%Y%m%d_%H%M%S).log - 重点搜索:
Found one Java-level deadlock—— 若存在,jstack 会直接列出循环等待的线程和锁地址 - 没搜到不等于没死锁:可能是跨层锁(DB 行锁 + 应用锁)、StampedLock、自定义同步器,jstack 无法识别
读堆栈:顺着 locked 和 waiting to lock 找闭环
定位关键字段:
立即学习“Java免费学习笔记(深入)”;
-
locked <0x...>:该线程当前持有的锁对象地址 -
waiting to lock <0x...>:它正在等待获取的另一个锁地址 - 顺着第一个地址,在 dump 文件中查找哪个线程在
locked <0x...>同一地址,同时又在waiting to lock前者持有的地址 - 结合 stack trace 中的类名、方法名、行号(如
OrderService.pay() line 87),定位到具体加锁位置
补验证:用 ThreadMXBean 主动检测(适合嵌入监控)
可在健康检查接口或定时任务中加入主动探测逻辑:
- 调用
ManagementFactory.getThreadMXBean().findDeadlockedThreads() - 返回非 null 数组即表示存在 Java 级死锁,可立即记录线程 ID 和堆栈
- 比人工 dump 更及时,适合做告警触发条件(如每 5 分钟扫一次,连续 2 次命中则发钉钉)
- 注意:仅检测 JVM 内部锁,对 DB、Redis、文件句柄等外部资源无效

















