Java 死锁排查

林欢喜 Java经验 发布时间:2025-12-23 15:18:25 阅读数:16271 1
下文笔者讲述使用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操作、远程调用等)
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202512/17664744058522.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者