欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用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和jstack快速定位线程状态
图-1

 

线程状态跟踪

在Java中,使用jps命令,查询正在运行的虚拟机java进程,一般显示信息就是,pid和进程名称。示例如下:

使用java命令jps和jstack快速定位线程状态
图-2

 使用jstack [pid] 输出当前进程的堆栈信息。主要有两种使用方式,如下:

  • 控制台输出堆栈信息 jstack pid,示例:
使用java命令jps和jstack快速定位线程状态
图-3
  • 将堆栈信息输出到执行文件 jstack pid > file。示例,输出pid 11840的进程堆栈信息存储到dump11840文件中,执行命令jstack 11840 > C:\Users\86151\Desktop\dump11840。结果如下:
使用java命令jps和jstack快速定位线程状态
图-4
使用java命令jps和jstack快速定位线程状态
图-5

堆栈信息分析:示例堆栈信息如图:

使用java命令jps和jstack快速定位线程状态
图-6
  1. 线程名称,ApplicationImpl pooled thred 450。
  2. 线程优先级。
  3. tid十六进制地址。
  4. 线程十六进制地址。
  5. 线程当前状态,TIMED_WAITING。
  6. 线程当前执行的方法,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的锁都没有释放,就产生了死锁。执行的程序日志输出如下:

使用java命令jps和jstack快速定位线程状态

堆栈信息如下:

使用java命令jps和jstack快速定位线程状态

上图中,代码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的状态的信息,如图:

使用java命令jps和jstack快速定位线程状态

相关标签: 开发工具