69-How to locate deadlock with command line and code?

Before that, we introduced what a deadlock is and the necessary conditions for a deadlock to occur. Of course, even if we write code very carefully, it is inevitable that deadlock may still occur. Once a deadlock occurs, the first step is to find it , because after finding and positioning the deadlock, In order to have the next remedial measures, such as removing the deadlock, recovering after the deadlock, optimizing the code, etc.; if the deadlock is not found, the next steps will be out of the question.

Let's take a look at how to find the deadlock using the command line.

Command: jstack

This command is called jstack, and it can see some information about our Java threads. If it is a relatively obvious deadlock relationship, then this tool can directly detect it; if the deadlock is not obvious, then it cannot be directly detected, but we can also use this to analyze the thread status, and then we can find the interdependence of locks Relationship , so this is also very helpful for us to find a way to deadlock.

Let's give it a try and execute this command.

First, let's run the MustDeadLock class that is bound to deadlock in Chapter 67 :

/**
 * 描述:     必定死锁的情况
 */
public class MustDeadLock implements Runnable {
    
    
    public int flag;
    static Object o1 = new Object();
    static Object o2 = new Object();
    public void run() {
    
    
        System.out.println("线程"+Thread.currentThread().getName() + "的flag为" + flag);
        if (flag == 1) {
    
    
            synchronized (o1) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o2) {
    
    
                    System.out.println("线程1获得了两把锁");
                }
            }
        }
        if (flag == 2) {
    
    
            synchronized (o2) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o1) {
    
    
                    System.out.println("线程2获得了两把锁");
                }
            }
        }
    }
    public static void main(String[] argv) {
    
    
        MustDeadLock r1 = new MustDeadLock();
        MustDeadLock r2 = new MustDeadLock();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1, "t1");
        Thread t2 = new Thread(r2, "t2");
        t1.start();
        t2.start();
     }
}

Because it has a deadlock, the program will not stop after running without our intervention; then open our terminal and execute the command ${JAVA_HOME}/bin/jps, you can view the current Java program pid, my execution results are as follows:

56402 MustDeadLock
56403 Launcher
56474 Jps
55051 KotlinCompileDaemon

There are multiple lines, you can see that the first line is the pid 56402 such as MustDeadLock; then we continue to execute the next command, ${JAVA_HOME}/bin/jstack add a space, and then enter the pid of this class we just got, That is 56402, so the complete command is ${JAVA_HOME}/bin/jstack 56402; in the end it will print a lot of information, including the information about the thread acquiring the lock, such as which thread acquires which lock, and which lock it acquired The important information obtained in the statement, what is the lock it is waiting for or holding, etc. , will be printed out. We intercept some useful information related to deadlock and show it as follows:

Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x00007fa06c004a18 (object 0x000000076adabaf0, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x00007fa06c007358 (object 0x000000076adabb00, a java.lang.Object),
  which is held by "t2"
Java stack information for the threads listed above:
===================================================
"t2":
	at lesson67.MustDeadLock.run(MustDeadLock.java:31)
	- waiting to lock <0x000000076adabaf0> (a java.lang.Object)
	- locked <0x000000076adabb00> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
"t1":
	at lesson67.MustDeadLock.run(MustDeadLock.java:19)
	- waiting to lock <0x000000076adabb00> (a java.lang.Object)
	- locked <0x000000076adabaf0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock

Here it will print "Found one Java-level deadlock" first, indicating "found a deadlock", and then more detailed information. From the information in the middle part, it can be seen that the t2 thread wants to get this tail. The lock object numbered af0, but it is held by the t1 thread, and t2 holds the lock object ending in b00; on the contrary, t1 wants to acquire the lock object ending in b00, but it is held by the t2 thread, and at the same time What t1 is holding is the lock object ending in af0, which forms a dependency loop and deadlock occurs. Finally, it also printed "Found 1 deadlock.". It can be seen that the jstack tool not only helped us find the deadlock, but also told us which thread, which lock we wanted to acquire, and what kind of loop was formed . When we have this information, the deadlock is very easy to locate, so we can further modify the code to avoid the deadlock.

The above is the method of using jstack to locate deadlocks. Jstack can be used to help us analyze the locks held by threads and the locks needed, and then analyze whether there is a deadlock caused by circular dependencies.

Code: ThreadMXBean

Let's take a look at the way to locate deadlocks with code.

We will use the ThreadMXBean tool class, the code example is as follows:

public class DetectDeadLock implements Runnable {
    
    
    public int flag;
    static Object o1 = new Object();
    static Object o2 = new Object();
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName()+" flag = " + flag);
        if (flag == 1) {
    
    
            synchronized (o1) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o2) {
    
    
                    System.out.println("线程1获得了两把锁");
                }
            }
        }
        if (flag == 2) {
    
    
            synchronized (o2) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o1) {
    
    
                    System.out.println("线程2获得了两把锁");
                }
            }
        }
    }
    public static void main(String[] argv) throws InterruptedException {
    
    
        DetectDeadLock r1 = new DetectDeadLock();
        DetectDeadLock r2 = new DetectDeadLock();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1,"t1");
        Thread t2 = new Thread(r2,"t2");
        t1.start();
        t2.start();
        Thread.sleep(1000);
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        if (deadlockedThreads != null && deadlockedThreads.length > 0) {
    
    
            for (int i = 0; i < deadlockedThreads.length; i++) {
    
    
                ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
                System.out.println("线程id为"+threadInfo.getThreadId()+",线程名为" + threadInfo.getThreadName()+"的线程已经发生死锁,需要的锁正被线程"+threadInfo.getLockOwnerName()+"持有。");
            }
        }
    }
}

This class is upgraded on the basis of the previous MustDeadLock class. The main function of the MustDeadLock class is to allow thread 1 and thread 2 to acquire the two locks o1 and o2 in a different order, and form a deadlock. In the main function, the code after starting t1 and t2 is our newly added code this time. We use Thread.sleep(1000) to ensure that a deadlock has formed, and then use ThreadMXBean to check the deadlock.

Through the findDeadlockedThreads method of ThreadMXBean, an array of deadlockedThreads can be obtained, and then judged. When the array is not empty and the length is greater than 0, we print out the corresponding thread information one by one. For example, if we print out the thread id and the thread name, we also print out which thread is holding the lock it needs , then the running result of this part of the code is as follows.

t1 flag = 1
t2 flag = 2
线程 id 为 12,线程名为 t2 的线程已经发生死锁,需要的锁正被线程 t1 持有。
线程 id 为 11,线程名为 t1 的线程已经发生死锁,需要的锁正被线程 t2 持有。

There are a total of four lines of statements. The first two lines are "t1 flag = 1" and "t2 flag = 2". This is the content printed before the deadlock occurs; the next two lines are the result of the deadlock we detected , You can see that it prints "Thread id is 12, the thread named t2 has deadlocked, and the required lock is being held by thread t1." Similarly, it will also print out "Thread id" It is 11, the thread named t1 has deadlocked, and the required lock is being held by thread t2."

It can be seen that ThreadMXBean can also help us find and locate deadlocks. If we add such detection to the business code, then we can locate in time when a deadlock occurs, and perform other processing such as alarms at the same time , which will also enhance The robustness of our program.

to sum up

To summarize below, we introduce two ways to locate the deadlock in the code. When a deadlock occurs, we can use the jstack command, or use ThreadMXBean in the code to help us find the deadlock.

Guess you like

Origin blog.csdn.net/Rinvay_Cui/article/details/111060244