乐趣区

关于java:troubleshoot之用controlbreak解决线程死锁问题

简介

如果咱们在程序中遇到线程死锁的时候,该怎么去解决呢?

本文将会从一个理论的例子登程,一步一步的揭开 java 问题解决的面纱。

死锁的代码

写过 java 多线程程序的人应该都晓得,多线程中一个很重要的事件就是状态的同步,然而在状态同步的过程中,一不小心就有可能会导致死锁的问题。

一个最简略的死锁状况就是 thread1 占有资源 1,而后又要去获取资源 2. 而 thread2 占有资源 2,又要去获取资源 1 的状况。

举个具体的例子:

public class TestDeadLock {public static Object lock1= new Object();
    public static Object lock2= new Object();
    public static void main(String[] args) {Runnable runnable1= ()-> {System.out.println("in lock1");
            synchronized(lock1){System.out.println("Lock1 lock obj1");
                try {Thread.sleep(3000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized(lock2){System.out.println("Lock1 lock obj2");
                }
            }
        };

        Runnable runnable2= ()-> {System.out.println("in lock2");
            synchronized(lock2){System.out.println("Lock2 lock obj2");
                try {Thread.sleep(3000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized(lock1){System.out.println("Lock2 lock obj1");
                }
            }
        };

        Thread a = new Thread(runnable1);
        Thread b = new Thread(runnable2);
        a.start();
        b.start();}
}

咱们运行下面的代码:

in lock1
Lock1 lock obj1
in lock2
Lock2 lock obj2

发送了锁循环期待的状况,程序执行不上来了,发送了死锁。

control+break 命令

在代码很简略的状况下,咱们很容易就能剖析进去死锁的起因,然而如果是在一个十分宏大的线上我的项目的时候,剖析代码就没有那么容易了。

怎么做呢?

明天教给大家一个办法,应用 control+break 命令。

control+break 在 linux 示意的是 Control+backslash,而在 Windows 上面就是 Control+Break 按钮。

当然,还有一个更加通用的就是应用:

kill -QUIT pid 命令。

咱们用 jps 命令获取到执行 java 程序的过程 id,而后执行 kill -QUIT 命令。

执行结束,咱们会发现运行的 java 过程会输入一些额定的日志,这些额定的日志就是咱们找出死锁的关键因素。

留神,这个 kill 命令并不会终止程序的运行。

输入的内容比拟多,咱们一部分一部分的解说。

Full thread dump

日志的第一局部就是 Full thread dump,蕴含了 JVM 中的所有线程的状态信息。

咱们看一下咱们代码中的两个要害线程信息:

"Thread-0" #13 prio=5 os_prio=31 cpu=4.86ms elapsed=230.16s tid=0x00007fc926061800 nid=0x6403 waiting for monitor entry  [0x0000700008d6a000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21)
    - waiting to lock <0x0000000787e868f0> (a java.lang.Object)
    - locked <0x0000000787e868e0> (a java.lang.Object)
    at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source)
    at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)

"Thread-1" #14 prio=5 os_prio=31 cpu=4.32ms elapsed=230.16s tid=0x00007fc924869800 nid=0x6603 waiting for monitor entry  [0x0000700008e6d000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36)
    - waiting to lock <0x0000000787e868e0> (a java.lang.Object)
    - locked <0x0000000787e868f0> (a java.lang.Object)
    at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source)
    at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)

下面的输入列出了线程名字,线程的优先级,cpu 工夫,是否是 daemon 线程,线程 ID,线程状态等有用的信息。

看到下面的输入,咱们看到两个线程都是处于 BLOCKED 状态,都在期待 object monitor。

还记得线程的几个状态吗?咱们再来温习一下。

死锁检测

接下来的局部就是咱们最关怀的死锁检测了。

Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x00007fc926807e00 (object 0x0000000787e868f0, a java.lang.Object),
  which is held by "Thread-1"

"Thread-1":
  waiting to lock monitor 0x00007fc926807f00 (object 0x0000000787e868e0, a java.lang.Object),
  which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-0":
    at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21)
    - waiting to lock <0x0000000787e868f0> (a java.lang.Object)
    - locked <0x0000000787e868e0> (a java.lang.Object)
    at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source)
    at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)
"Thread-1":
    at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36)
    - waiting to lock <0x0000000787e868e0> (a java.lang.Object)
    - locked <0x0000000787e868f0> (a java.lang.Object)
    at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source)
    at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)

Found 1 deadlock.

下面的日志咱们能够很显著的看进去,两个线程别离取得了对方须要的锁,所以导致了死锁。

同时还具体的列出了 thread stack 的信息,供咱们剖析。

如果咱们增加了参数 -XX:+PrintConcurrentLocks,还会输入各个线程的取得的 concurrent lock 信息。

Heap 信息

最初一部分是 Heap 的统计信息:

Heap
 garbage-first heap   total 133120K, used 3888K [0x0000000780000000, 0x0000000800000000)
  region size 1024K, 4 young (4096K), 0 survivors (0K)
 Metaspace       used 1122K, capacity 4569K, committed 4864K, reserved 1056768K
  class space    used 108K, capacity 412K, committed 512K, reserved 1048576K

如果咱们增加了 -XX:+PrintClassHistogram 命令,还能够额定的输入 class 直方图统计信息。

总结

下面就是应用 Control+Break 命令来剖析 java 死锁问题的具体例子,心愿大家可能喜爱。

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/jvm-diagnostic-control-break/

本文起源:flydean 的博客

欢送关注我的公众号: 程序那些事,更多精彩等着您!

退出移动版