前言

    有时候我们发现应用卡住了,通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题,arthas提供了thread命令,协助我们快速定位。

    下面我们用个案例去演示一下这个死锁场景,然后用arthas去定位出这个问题。

模拟死锁的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RestController
@RequestMapping("/api/")
public class TestController {
private Object obj1 = new Object();
private Object obj2 = new Object();

@GetMapping(value = "/test")
public Response hb() {

new Thread(() -> {
synchronized (obj1) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
synchronized (obj2) {
System.out.println("[dead]执行内容111...");
}
}
}).start();

new Thread(() -> {
synchronized (obj2) {
synchronized (obj1) {
System.out.println("[dead]执行内容222...");
}
}
}).start();
return Response.success();
}
}

通过thread命令定位

1,直接使用”thread“命令,输出线程统计信息。其中:BLOCKED 表示目前阻塞的线程数。

1
thread

命令效果:

1
2
3
4
5
6
7
8
9
Threads Total: 76, NEW: 0, RUNNABLE: 19, BLOCKED: 999, WAITING: 18, TIMED_WAITING: 15, TERMINATED: 0,
Internal threads: 10
ID NAME GROUP PRIORIT STATE %CPU DELTA_T TIME INTERRUP DAEMON
88 sentinel-time-tick-threa main 5 TIMED_W 3.14 0.006 0:3.618 false true
118 arthas-command-execute system 5 RUNNABL 1.43 0.002 0:0.014 false true
-1 C1 CompilerThread2 - -1 - 0.16 0.000 0:3.077 false true
-1 VM Periodic Task Thread - -1 - 0.06 0.000 0:0.078 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.017 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.011 false true

2,执行“thread -b”命令,找出当前阻塞其他线程的线程,即造成死锁的罪魁祸首

1
thread -b

命令效果:

1
2
3
4
5
6
"Thread-39" Id=93 BLOCKED on java.lang.Object@1820d666 owned by "Thread-38" Id=92
at cn.soilove.expro.controller.TestController.lambda$hb$1(TestController.java:43)
- blocked on java.lang.Object@1820d666
- locked java.lang.Object@29064ce5 <---- but blocks 7 other threads!
at cn.soilove.expro.controller.TestController$$Lambda$1407/395258860.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)

注:上面这个命令直接输出了 造成死锁的线程ID,和具体的代码位置,以及当前线程一共阻塞的线程数量:“<—- but blocks 7 other threads!“。是不是很优秀?现在,我们只需要去修改掉这的代码问题即可。

3,其他线程命令:

1
2
3
4
thread –all, 显示所有的线程;
thread id, 显示指定线程的运行堆栈;
thread –state:查看指定状态的线程,如:thread –state BLOCKED;
thread -n 3:展示当前最忙的前N个线程并打印堆栈;