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

线程状态定义

在线上项目中,当程序处于长时间停顿的时候,可以使用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;
    }

线程之间的状态转换如图:

图-1

线程状态跟踪

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

图-2

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

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

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

图-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的锁都没有释放,就产生了死锁。执行的程序日志输出如下:

堆栈信息如下:

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

发布了37 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/new_com/article/details/104533550