使用java命令jps和jstack快速定位线程状态
程序员文章站
2024-03-18 10:08:28
...
线程状态定义
在线上项目中,当程序处于长时间停顿的时候,可以使用java提供的jstack命令跟踪正在执行方法的堆栈情况,jstack能够生成虚拟机当前时刻的线程堆栈情况。主要,监控线程的状态,判断出线程停顿的原因。例如,死锁,死循环,多个线程等待等等。线程的状态包括NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED,其源码如下:
public enum State {
/**
* Thread state for a thread which has not yet started.
* 线程创建后尚未启动的线程处于这种状态
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* Runable包括了操作系统线程状态中的Running和Ready, 也就是处于此
状态的线程有可能正在执行, 也有可能正在等待着CPU为它分配执行时间
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* 线程被阻塞了, “阻塞状态”与“等待状态”的区别是: “阻塞状态”在等
待着获取到一个排他锁, 这个事件将在另外一个线程放弃这个锁的时候发生; 而“等待状
态”则是在等待一段时间, 或者唤醒动作的发生。 在程序等待进入同步区域的时候, 线程将
进入这种状态。
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* 无限期等待状态,处于这种状态的线程不会被分配CPU执行时间,它们要等待被
其他线程显式地唤醒
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* 线程等待被唤醒,处于这种状态的线程也不会被分配CPU执行时间, 不过无
须等待被其他线程显式地唤醒, 在一定时间之后它们会由系统自动唤醒
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* 线程已经执行结束
*/
TERMINATED;
}
线程之间的状态转换如图:
线程状态跟踪
在Java中,使用jps命令,查询正在运行的虚拟机java进程,一般显示信息就是,pid和进程名称。示例如下:
使用jstack [pid] 输出当前进程的堆栈信息。主要有两种使用方式,如下:
- 控制台输出堆栈信息 jstack pid,示例:
- 将堆栈信息输出到执行文件 jstack pid > file。示例,输出pid 11840的进程堆栈信息存储到dump11840文件中,执行命令jstack 11840 > C:\Users\86151\Desktop\dump11840。结果如下:
堆栈信息分析:示例堆栈信息如图:
- 线程名称,ApplicationImpl pooled thred 450。
- 线程优先级。
- tid十六进制地址。
- 线程十六进制地址。
- 线程当前状态,TIMED_WAITING。
- 线程当前执行的方法,park。
示例常见停顿场景
- 死锁场景
在线程嵌套的获取锁,就有可能产生死锁,如下给出死锁的代码示例,和堆栈信息。代码示例如下:
public class DeadLockDemo {
private static String a = "a";
private static String b = "b";
private void deadLock() {
Thread t1 = new Thread(() -> {
synchronized (a) {
System.out.println("get a lock thread " + Thread.currentThread().getName());
try {
// 延时2秒
Thread.sleep(2000L);
} catch (Exception e) {
}
synchronized (b) {
System.out.println("get b lock thread " + Thread.currentThread().getName());
}
}
});
Thread t2 = new Thread(() -> {
synchronized (b) {
System.out.println("get b lock thread " + Thread.currentThread().getName());
synchronized (a) {
System.out.println("get a lock thread " + Thread.currentThread().getName());
}
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
}
程序运行后,线程t1获取a的锁,线程t2获取b的锁。然后,当线程2尝试获取a的锁时,线程t1尝试获取b,由于此时a和b的锁都没有释放,就产生了死锁。执行的程序日志输出如下:
堆栈信息如下:
上图中,代码35行和代码24行引起了死锁。
- 长时间等待
线程长时间,分配不到cpu而进入等待状态。下面模拟一个线程在执行,大批量线程在等待的场景,示例代码如下:
public class LongWaitDemo {
private void longWait() {
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
while (true) {
System.out.println("executing thread " + Thread.currentThread().getName());
try {
// 延时2000秒
Thread.sleep(2000000L);
} catch (Exception e) {
}
}
});
thread.start();
}
Thread t2 = new Thread(() -> {
while (true) {
try {
// 延时1秒
Thread.sleep(1000L);
} catch (Exception e) {
}
System.out.println("executing thread " + Thread.currentThread().getName());
}
});
t2.start();
}
public static void main(String[] args) {
new LongWaitDemo().longWait();
}
}
跟踪其堆栈信息,会发现几乎全是TIMED_WAITING的状态的信息,如图:
上一篇: ELK安装
下一篇: 利用EASYX库做的约瑟夫环演示