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

导读:本篇文章讲解 使用java命令jps和jstack快速定位线程状态,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

线程状态定义

在线上项目中,当程序处于长时间停顿的时候,可以使用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快速定位线程状态

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13634.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!