69-コマンドラインとコードでデッドロックを見つける方法は?

その前に、デッドロックとは何か、デッドロックが発生するための必要条件を紹介しました。もちろん、コードを注意深く記述しても、デッドロックが発生することは避けられません。デッドロックが発生すると、最初のステップはデッドロックを見つけることです。デッドロックを見つけて配置した後、次の改善策を講じるために、デッドロックの削除、デッドロック後の回復、コードの最適化など。デッドロックが見つからない場合、次の手順は問題外になります。

コマンドラインを使用してデッドロックを見つける方法を見てみましょう。

コマンド:jstack

このコマンドはjstackと呼ばれ、Javaスレッドに関するいくつかの情報を見ることができます。比較的明白なデッドロック関係である場合、このツールはそれを直接検出できます。デッドロックが明白でない場合、直接検出することはできませんが、これを使用してスレッドステータスを分析すると、相互依存関係を見つけることができます。ロックの関係なので、これはデッドロックの方法を見つけるのにも非常に役立ちます。

試してみて、このコマンドを実行してみましょう。

まず、第67章で説明しMustDeadLockクラスを実行してみましょう。これは、デッドロックにバインドされています。

/**
 * 描述:     必定死锁的情况
 */
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();
     }
}

デッドロックがあるため、プログラムは介入なしで実行した後も停止しません。ターミナルを開いてコマンド$ {JAVA_HOME} / bin / jpsを実行すると、現在のJavaプログラムpidを表示できます。実行結果は次のとおりです。

56402 MustDeadLock
56403 Launcher
56474 Jps
55051 KotlinCompileDaemon

複数の行があり、最初の行がMustDeadLockなどのpid 56402であることがわかります。次に、次のコマンド$ {JAVA_HOME} / bin / jstack add a spaceを実行し、このクラスのpidを入力します。取得したばかり、つまり56402なので、完全なコマンドは$ {JAVA_HOME} / bin / jstack 56402です。最終的には、どのスレッドがどのロックを取得するかなど、ロックを取得するスレッドに関する情報を含む多くの情報が出力されます。 、どのロックを取得したかステートメントで取得した重要な情報、待機中または保持中のロックなどが出力されます。デッドロックに関連するいくつかの有用な情報を傍受し、次のように表示します。

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

ここでは、最初に「Found one Java-level deadlock」を出力し、「found a deadlock」を示し、次に詳細情報を出力します。中央の情報から、t2スレッドがこのテールを取得しようとしていることがわかります。番号af0のロックオブジェクトですが、t1スレッドによって保持され、t2はb00で終わるロックオブジェクトを保持します。逆に、t1はb00で終わるロックオブジェクトを取得したいのですが、t2スレッドによって保持されます。同時に、t1が保持しているのは、af0で終わるロックオブジェクトです。これは、依存関係ループを形成し、デッドロックが発生します。最後に、「Found 1 deadlock。」も出力されました。jstackツールは、デッドロックを見つけるのに役立つだけでなく、どのスレッド、どのロックを取得したいか、どのようなループ形成されたかを教えてくれたことがわかります。この情報があれば、デッドロックを見つけるのは非常に簡単なので、コードをさらに変更してデッドロックを回避できます。

上記は、jstackを使用してデッドロックを特定する方法です。Jstackを使用すると、スレッドによって保持されているロックと必要なロックを分析し、循環依存によってデッドロックが発生しているかどうかを分析できます。

コード:ThreadMXBean

コードでデッドロックを見つける方法を見てみましょう。

ThreadMXBeanツールクラスを使用します。コード例は次のとおりです。

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()+"持有。");
            }
        }
    }
}

このクラスは、以前のMustDeadLockクラスに基づいてアップグレードされます。MustDeadLockクラスの主な機能は、スレッド1とスレッド2が2つのロックo1とo2を異なる順序で取得し、デッドロックを形成できるようにすることです。main関数では、t1とt2を開始した後のコードが、今回新しく追加されたコードです。Thread.sleep(1000)を使用してデッドロックが形成されていることを確認してから、ThreadMXBeanを使用してデッドロックをチェックします。

ThreadMXBeanのfindDeadlockedThreadsメソッドを使用して、deadlockedThreadsの配列を取得し、判断することができます。配列が空でなく、長さが0より大きい場合、対応するスレッド情報を1つずつ出力します。たとえば、スレッドIDとスレッド名を出力する場合、必要なロックを保持しているスレッドも出力すると、コードのこの部分の実行結果は次のようになります。

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

合計4行のステートメントがあります。最初の2行は「t1flag = 1」と「t2flag = 2」です。これはデッドロックが発生する前に出力されるコンテンツです。次の2行は、デッドロックの結果です。検出され、「スレッドIDは12、t2という名前のスレッドはデッドロックされ、必要なロックはスレッドt1によって保持されています」と出力されます。同様に、「スレッドID」も出力されます。これは11、スレッドです。名前付きt1がデッドロックし、必要なロックがスレッドt2によって保持されています。」

ThreadMXBeanも見つけて、デッドロックを見つける私たちを助けることができることがわかる。我々はビジネスコードにこのような検出を追加した場合、我々は、デッドロックが発生したときの時間に見つけ、できると同時に、アラームなど他の処理を実行し、その意志また、プログラムの堅牢性を強化します。

総括する

以下に要約すると、コード内のデッドロックを見つける2つの方法を紹介します。デッドロックが発生した場合、jstackコマンドを使用するか、コード内でThreadMXBeanを使用してデッドロックを見つけることができます。

おすすめ

転載: blog.csdn.net/Rinvay_Cui/article/details/111060244