一、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占用过高。