原创|《菜鸟读并发》多线程程序问题如何调试?

点击上方“码农进阶之路”,选择设为星标

回复面经获取面试资料

原创|《菜鸟读并发》多线程程序问题如何调试?_第1张图片

多线程程序问题如何调试?

  • 出了 Bug 基本上都是靠日志,靠线程dump来跟踪问题

  • 分析线程dump的一个基本功就是分析线程状态,大部分的死锁、饥饿、活锁问题都需要跟踪分析线程的状态。

  • 你可以通过jstack命令或者VisualVM这个可视化工具将JVM所有的线程栈信息导出来,完整的线程栈信息不仅包括线程的当前状态、调用栈,还包括了锁的信息

  • 例如一个死锁的程序,导出的线程栈明确告诉我发生了死锁,并且将死锁线程的调用栈信息清晰地显示出来了

  • 导出线程栈,分析线程状态是诊断并发问题的一个重要工具

top命令

可以通过top命令查看各个进程的cpu使用情况,默认按cpu使用率排序

原创|《菜鸟读并发》多线程程序问题如何调试?_第2张图片

上图中可以看出pid为23344的java进程占用了较多的cpu资源;

通过top -Hp 23344可以查看该进程下各个线程的cpu使用情况

原创|《菜鸟读并发》多线程程序问题如何调试?_第3张图片上图中可以看出pid为25077的线程占了较多的cpu资源

jstack命令

利用jstack命令查看该线程当前的堆栈状态,jstack命令指定pid和输出的文件位置,主要用来获取thread的dump线程信息,包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈

原创|《菜鸟读并发》多线程程序问题如何调试?_第4张图片

步骤:

  1. 首先在top命令中,已经获取到了占用cpu资源较高的线程pid

  2. 将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;

  3. 隔段时间再执行一次jstack命令获取thread dump,区分两份dump是否有差别

  4. 在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题

通过thread dump分析线程状态

除了上述的分析,大多数情况下会基于thead dump分析当前各个线程的运行情况,如是否存在死锁、是否存在一个线程长时间持有锁不放等等。

在dump中,线程一般存在哪几种状态:
  • 1)RUNNABLE,线程处于执行中

  • 2)BLOCKED,线程被阻塞

  • 3)WAITING,线程正在等待

实例1:多线程竞争synchronized锁原创|《菜鸟读并发》多线程程序问题如何调试?_第5张图片

很明显:线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态

  1. locked <0x000000076bf62208>说明线程1对地址为0x000000076bf62208对象进行了加锁;

  2. waiting to lock <0x000000076bf62208> 说明线程2在等待地址为0x000000076bf62208对象上的锁

  3. waiting for monitor entry [0x000000001e21f000]说明线程1是通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor,具体实现可以参考深入分析synchronized的JVM实现;

实例2:通过wait挂起线程
static class Task implements Runnable {
    @Override
    public void run() {
        synchronized (lock) {
            try {
                lock.wait();
                //TimeUnit.SECONDS.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

dump结果原创|《菜鸟读并发》多线程程序问题如何调试?_第6张图片

线程1和2都处于WAITING状态

  1. 线程1和2都是先locked <0x000000076bf62500>,再waiting on <0x000000076bf62500>,之所以先锁再等同一个对象,是因为wait方法需要先通过synchronized获得该地址对象的monitor;

  2. waiting on <0x000000076bf62500>说明线程执行了wait方法之后,释放了monitor,进入到"Wait Set"队列,等待其它线程执行地址为0x000000076bf62500对象的notify方法,并唤醒自己,具体实现可以参考深入分析Object.wait/notify实现机制;

如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。 

 原创不易,欢迎转发,求个关注,文末右下角赏个"在看"吧。

往期精选

上一篇:原创|《菜鸟读并发》什么是线程死锁,怎么解决线程死锁

下一篇:原创|《菜鸟读并发》解读vmstat查看线程上线文的使用

码农进阶之路

长按二维码关注 

原创|《菜鸟读并发》多线程程序问题如何调试?_第7张图片

面经 | 原理 | 源码 | 实战 | 工具

点击留言

你可能感兴趣的:(原创|《菜鸟读并发》多线程程序问题如何调试?)