troubleshoot之:用control+break解决线程死锁问题

简介: troubleshoot之:用control+break解决线程死锁问题

目录



简介


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


本文将会从一个实际的例子出发,一步一步的揭开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。

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


image.png

死锁检测


接下来的部分就是我们最关心的死锁检测了。


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死锁问题的具体例子,希望大家能够喜欢。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2天前
|
监控 Linux 编译器
多线程死锁检测的分析与实现(linux c)-有向图的应用
在日常的软件开发中,多线程是不可避免的,使用多线程中的一大问题就是线程对锁的不合理使用造成的死锁,死锁一旦发生,将导致多线程程序响应时间长,吞吐量下降甚至宕机崩溃,那么如何检测出一个多线程程序中是否存在死锁呢?在提出解决方案之前,先对死锁产生的原因以及产生的现象做一个分析。最后在用有向环来检测多线程中是否存在死锁的问题。
59 0
|
7月前
|
存储 Java
java之线程死锁和ThreadLocal的使用
java之线程死锁和ThreadLocal的使用
|
2天前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
44 1
|
2天前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
72 3
|
2天前
|
Java
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
25 1
|
2天前
|
算法 安全 Linux
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
12 0
|
2天前
|
安全 算法 关系型数据库
线程安全--深入探究线程等待机制和死锁问题
线程安全--深入探究线程等待机制和死锁问题
|
2天前
|
算法 Java 编译器
【JavaEE多线程】掌握锁策略与预防死锁
【JavaEE多线程】掌握锁策略与预防死锁
24 2
|
7月前
|
Java 开发者
解锁Java多线程编程中的死锁之谜
解锁Java多线程编程中的死锁之谜
37 0
|
2天前
|
存储 监控 程序员
线程死锁检测组件逻辑与源码
线程死锁检测组件逻辑与源码
68 2