Java 死锁排查
下文笔者讲述使用jstack和线程id对死锁进行排查和处理,如下所示
死锁排查方法
Java死锁排查的核心步骤为:
获取Java进程ID(PID)
导出线程堆栈信息
定位死锁线程与关联资源
分析死锁原因
======================================================
借助`jstack` 是JDK自带的线程堆栈分析工具
无需额外安装
直接在命令行使用即可
例:排查死锁
Windows系统(cmd/PowerShell)查看进程方法
列出所有Java进程(包含PID和主类名)
jps -l
若jps无效,可使用任务管理器查看,或用:
tasklist | findstr java
Linux/Mac系统(终端)查看进程方法
#列出所有Java进程(推荐,简洁直观)
jps -l
# 备选方案:列出所有进程并过滤java相关
ps -ef | grep java
使用jstack导出线程堆栈信息
通过PID导出该Java进程的完整线程堆栈,有两种使用方式:
方式1:直接输出到控制台(适合快速查看)
jstack <PID> # 替换<PID>为步骤1获取的进程ID,如jstack 12345
方式2:输出到文件(适合复杂堆栈分析,推荐)
# Windows/Linux通用,将堆栈信息写入jstack_deadlock.log文件
jstack <PID> > jstack_deadlock.log
步骤3:定位死锁线程(两种核心方式)
方式1:jstack自动识别死锁(快速定位)
`jstack` 工具会自动检测死锁,
并在堆栈信息末尾标注 `Found one Java-level deadlock`,
直接展示死锁线程、锁资源及持有关系,无需手动查找线程ID
例
Found one Java-level deadlock:
=============================
"ThreadB":
waiting to lock monitor 0x0000000016b5e668 (object 0x000000076ab10160, a java.lang.Object),
which is held by "ThreadA"
"ThreadA":
waiting to lock monitor 0x0000000016b612f8 (object 0x000000076ab10170, a java.lang.Object),
which is held by "ThreadB"
Java stack information for the threads listed above:
===================================================
"ThreadB":
at com.example.DeadLockDemo.run(DeadLockDemo.java:25)
- waiting to lock <0x000000076ab10160> (a java.lang.Object)
- locked <0x000000076ab10170> (a java.lang.Object)
"ThreadA":
at com.example.DeadLockDemo.run(DeadLockDemo.java:15)
- waiting to lock <0x000000076ab10170> (a java.lang.Object)
- locked <0x000000076ab10160> (a java.lang.Object)
方式2:线程ID(TID)手动映射定位(适用于未自动识别场景)
当死锁未被jstack自动识别时,需要通过 线程ID(TID)手动关联,
核心步骤如下:
1. 获取线程的10进制TID:
- 若通过日志打印了线程ID(如 `Thread.currentThread().getId()`),
直接记录该10进制数字;
- 若在Linux系统中,可通过 `top -Hp <PID>`
查看该Java进程下的所有线程,
找到占用CPU/资源异常的线程(死锁线程通常处于阻塞状态),
记录其10进制TID(列名为PID,实际是线程ID)。
2. 将10进制TID转换为16进制:
因为jstack堆栈信息中,线程ID是以16进制形式存储的(前缀通常为`0x`),
需要进行进制转换:
- Windows:在cmd中执行 `calc`,打开计算器,切换到“程序员”模式,输入10进制TID,切换为16进制;
- Linux/Mac:在终端执行 `printf "%x\n" <10进制TID>`,直接输出16进制结果(无需加`0x`前缀)。
3. 在jstack堆栈中查找16进制线程ID:
打开jstack日志文件,搜索转换后的16进制字符串(如10进制TID为123,16进制为7b,搜索`7b`或`0x7b`),
找到对应的线程信息,查看线程状态(死锁线程通常状态为`BLOCKED`)和锁持有情况。
步骤4:分析死锁原因与解决
通过jstack堆栈信息,重点关注以下内容:
- 死锁线程的名称(`"ThreadA"`、`"ThreadB"`);
- 线程的状态(`BLOCKED` 表示阻塞,死锁的核心状态);
- 锁资源的标识(如 `<0x000000076ab10160>`,唯一标识一个锁对象);
- `waiting to lock`:表示线程正在等待获取某个锁;
- `locked`:表示线程已经成功获取了某个锁。
死锁的核心原因分析
死锁核心原因是:
多个线程互相持有对方需要的锁,
且都不释放自己已持有的锁
导致无限阻塞
死锁解决思路
1. 统一锁的获取顺序(所有线程按相同的顺序获取锁,避免交叉等待);
2. 给锁获取添加超时时间(如使用 `Lock.tryLock(long time, TimeUnit unit)`,
超时后放弃获取并释放已持有的锁);
3. 减少锁的粒度(避免使用全局锁,拆分锁资源,降低锁竞争概率);
4. 避免在锁内调用可能阻塞的方法(如IO操作、远程调用等)
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


