一、Thread Dump日志分析

接下来给大家再讲点在工作中比较实用的一个内容。就是我们在使用线程的时候,如果出现问题,怎么 排查?比如说

  • CPU占用率很高,响应很慢
  • CPU占用率不高,但响应很慢
  • 线程出现死锁的情况

演示代码

为了更好的体现效果,我们通过以下代码进行演示。

编写一个java项目

/**
 * 控制器接口层
 **/
@RestController
public class ThreadController {

    @GetMapping("/loop")
    public String dumpWhile(){
        new Thread(new WhileThread()).start();
        return "ok";
    }

    @GetMapping("/dead")
    public String dumpDeadLock(){
        Thread a = new ThreadRunA();
        Thread b = new ThreadRunB();
        a.start();
        b.start();
        return "ok";
    }
}
class WhileThread implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("Thread");
        }
    }
}
/**
 * 线程A
 **/
class ThreadRunA extends Thread {
    @Override
    public void run() {
        System.out.println("================A===================");
        synchronized (A.A) {
            System.out.println("我要开始执行任务A。。。。" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (B.B) {
            }
            System.out.println("我在执行任务结束了A。。。。" + Thread.currentThread().getName() + ":" + B.B.hashCode() + ":"
                    + A.A.hashCode());
        }
    }
}
/**
 * 线程B
 **/
class ThreadRunB extends Thread {
    @Override
    public void run() {
        System.out.println("================B===================");
        synchronized (B.B) {
            System.out.println("我要开始执行任务B。。。。" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (A.A) {
            }
            System.out.println("我在执行任务结束了B。。。。" + Thread.currentThread().getName() + ":" + B.B + ":" + A.A);
        }
    }
}
nohup java -jar -Dserver.port=8088 thread-demo-0.0.1-SNAPSHOT.jar > all.log &
//打成jar后运行

CPU占用率不高,但响应很慢

通过 curl http://127.0.0.1:8088/dead 演示死锁的场景

查看死锁问题的操作步骤如下:

  • 通过 jps命令,查看java进程的pid
  • 通过 jstack 查看线程日志

如果存在死锁情况,Thread Dump 日志里面肯定会给出 Found one Java-level deadlock:信息。只要 找到这个信息就可以立马定位到问题并且去解决。

Found one Java-level deadlock:
=============================
"Thread-1":
    waiting to lock monitor 0x0000000026070c88 (object 0x00000007163b7d78, a java.lang.Integer),
    which is held by "Thread-0"
"Thread-0":
    waiting to lock monitor 0x00000000260735c8 (object 0x0000000716649aa8, a java.lang.Integer),
    which is held by "Thread-1"

CPU占用率很高,响应很慢

有的时候我们会发现CPU占用率很高,系统日志也看不出问题,那么这种情况下,我们需要去看一下运 行中的线程有没有异常。

执行 curl http://127.0.0.1:8088/loop这个方法,会出现一个线程死循环的情况。

  • 通过 top -c 动态显示进程及占用资源的排行榜,从而找到占用CPU最高的进程PID,得到的

    PID=80972

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    
    80972 root      20   0 7773456 296124  12904 S 100.2  1.8   0:38.83 java
  • 然后再定位到对应的线程, top -H -p 80972 查找到该进程中最消耗CPU的线程,得到

    PID=81122

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
    81122 root 20 0 7773456 258504 12932 R 99.8 1.6 5:56.34 java 
    80972 root 20 0 7773456 258504 12932 S 0.0 1.6 0:00.00 java
  • 通过 printf "0x%x\n" 81122 命令,把对应的线程PID转化为16进制

    [root@localhost test]# printf "0x%x\n" 81122 
    0x13ce2
  • 截止执行这个命令 jstack 80972 | grep -A 20 0x13ce2 查看线程Dump日志,其中-A 20表示展示20行, 80972表示进程ID, 0x13ce2表示线程ID

从上述内容可以看出,是WhileThread.run方法中,执行的逻辑导致CPU占用过高。

最后修改:2022 年 06 月 23 日
如果觉得我的文章对你有用,请点个赞吧~