【工作随手记】deaklock排查

生产环境当中还没真正遇到过死锁的问题。有些疑似死锁的问题,后来经过排查也只是其它问题导致的。所以通过jstack到底怎样排查死锁问题有点疏忽了。这里作个记录。

模拟一个死锁

顺便复习一下。

死锁的产生有四个必要的条件

互斥使用,即当资源被一个线程占用时,别的线程不能使用
不可抢占,资源请求者不能强制从资源占有者手中抢夺资源,资源只能由占有者主动释放
请求和保持,当资源请求者在请求其他资源的同时保持对原因资源的占有
循环等待,多个线程存在环路的锁依赖关系而永远等待下去,例如T1占有T2的资源,T2占有T3的资源,T3占有T1的资源,这种情况可能会形成一个等待环路
对于死锁产生的四个条件只要能破坏其中一条即可让死锁消失,但是条件一是基础,不能被破坏。

模拟一个死锁。

 private static String lock1 = "lock1";
    private static String lock2 = "lock2";

    public static void main(String[] args) {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock1){
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + " 锁住了lock1");
                    synchronized (lock2){
                        System.out.println(Thread.currentThread() + " 锁住了lock2");
                    }
                }
            }
        };

        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock2){
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + " 锁住了lock2");
                    synchronized (lock1){
                        System.out.println(Thread.currentThread() + " 锁住了lock1");
                    }
                }
            }
        };

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.submit(r1);
        executorService.submit(r2);

    }

执行输出

Thread[pool-1-thread-1,5,main] 锁住了lock1
Thread[pool-1-thread-2,5,main] 锁住了lock2

后面一直卡住,通过idea查看堆栈信息可以看到,两个线程互相一直在等待对方释放锁。

"pool-1-thread-2" #13 prio=5 os_prio=0 tid=0x000000001ebc6000 nid=0xcf950 waiting for monitor entry [0x00000000207fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.nyp.test.DeadlockTest$2.run(DeadlockTest.java:49)

  • waiting to lock <0x000000076b19de70> (a java.lang.String)
  • locked <0x000000076b19dea8> (a java.lang.String)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

"pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x000000001ebc5000 nid=0xcfa20 waiting for monitor entry [0x00000000206ff000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.nyp.test.DeadlockTest$1.run(DeadlockTest.java:31)

  • waiting to lock <0x000000076b19dea8> (a java.lang.String)
  • locked <0x000000076b19de70> (a java.lang.String)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

通过idea我们很方便的观察到了两个线程在等待对方释放锁,而且通过观察其它的堆栈信息我们也能方便的知道,两个线程也分别锁住了对方想要申请的锁,因此造成了死锁。

但是在生产环境中,通过jstack会打印出一大堆线程的信息,而且只有有并发环境必然会上锁,堆栈信息当中必然会出现waiting for monitor``waiting on condition``locked等信息,这并不是死锁的完全充要条件。

将代码放到生产环境。通过jstack pid命令,可以看到会出现明确的deadlock的信息。

Found one Java-level deadlock:

"pool-4-thread-2":
waiting to lock monitor 0x00007f0c24026408 (object 0x00000005d0e7a708, a java.lang.String),
which is held by "pool-4-thread-1"
"pool-4-thread-1":
waiting to lock monitor 0x00007f0c24025c78 (object 0x00000005d0e7a740, a java.lang.String),
which is held by "pool-4-thread-2"

Java stack information for the threads listed above:

"pool-4-thread-2":
at com.alpha.data.util.DeadlockTest$2.run(DeadlockTest.java:49)

  • waiting to lock <0x00000005d0e7a708> (a java.lang.String)
  • locked <0x00000005d0e7a740> (a java.lang.String)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:750)
    "pool-4-thread-1":
    at com.alpha.data.util.DeadlockTest$1.run(DeadlockTest.java:31)
  • waiting to lock <0x00000005d0e7a740> (a java.lang.String)
  • locked <0x00000005d0e7a708> (a java.lang.String)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:750)

Found 1 deadlock.

也就是在生产环境中,通过jstack排查死锁问题时 ,只需要盯着deadlock字样即可,如果有死锁会明显的提示出产生死锁的代码所在。否则,便是没有死锁。

顺便复习一下通过jstack排查cpu占用高的问题

1.通过top命令找到cpu占用高的应用程序进程
2.通过top -H -p pid查看该应用中占用CPU高的线程。
3.通过printf "%x\n" pid 将线程高的线程号转为十六进制。
4.通过jstack过滤该十六进制的关键信息。jstack pid | grep 十六进制 -c 10

这样就可以看到占用CPU高的代码位置。

总结:就是先查到占用高的应用和具体的线程,然后根据线程到堆积信息查找即可。
不过堆栈信息非十进制,需提前把线程号转为十六进制。

posted @ 2023-01-30 11:29  是奉壹呀  阅读(275)  评论(0编辑  收藏  举报