美文网首页better
Java 如何快速排查死锁?

Java 如何快速排查死锁?

作者: PC_Repair | 来源:发表于2020-07-08 10:28 被阅读0次

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。

(1)实例

死锁的本质,举个例子如果此时有一个线程 A ,按照先获持有锁 a 再获取锁 b的顺序获得锁,同时另外一个线程 B,按照先获取锁 b 再获取锁 a 的顺序获取锁。如下图所示。

image-20200708093303203.png

代码模拟上述死锁过程:

import java.util.concurrent.TimeUnit;

public class DeadLock {

    private static Object lockA = new Object();
    private static Object lockB = new Object();

    public void deadLock() {
        Thread threadA = new Thread(() -> {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + "获取 lockA 成功");
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + "尝试获取 lockB");
                    synchronized (lockB) {
                        System.out.println(Thread.currentThread().getName() + "获取 lockB 成功");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "获取 lockB 成功");
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + "尝试获取 lockA");
                    synchronized (lockA) {
                        System.out.println(Thread.currentThread().getName() + "获取 lockA 成功");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();
        threadB.start();
    }

    public static void main(String[] args) {
        DeadLock deadLock = new DeadLock();
        deadLock.deadLock();
    }
}
deadlock_02.png
(2)通过 jdk 工具 jps、jstack 排查死锁问题

1)使用 jsp 查找程序进行

jps 是 jdk 提供的一个工具,可以查看正在运行的 java 进程。

➜  ~ jps 
57248 Jps
56736 Launcher
56737 DeadLock  # 死锁演示进程
59618

2)使用 jstack 查看线程堆栈信息

jstack 是 jdk 提供的一个工具,可以查看 java 进程中线程堆栈信息。更详细的用法见文档最后。

➜  ~ jstack 56737
deadlock_03.png

从上可以看出死锁的数量以及死锁在代码中出现的位置。

(3)通过 jdk 提供的工具 jconsole 排查死锁问题

jconsole 是 jdk 提供的一个可视化的工具,方便排查程序的一些问题,如:程序内存溢出、死锁问题等等。更详细的用法见文档最后。

jconsole 位于 jdk 的 bin 目录中

➜  bin ./jconsole
deadlock_04.png

上图可以看到我们的程序,点击 connect。

在 jconsole 窗口中查看线程堆栈信息。

deadlock_06.png

点击 DetectDeadlock 可以查看详细的死锁信息,和 jstack 展示的类似。

deadlock_08.png
如何避免死锁

我们知道了死锁如何产生的,那么就知道该如何去预防。如果一个线程每次只能获取一个锁,那么就不会出现由于嵌套持有锁顺序导致的死锁

1)正确的顺序获得锁

上面的例子出现死锁的根本原因就是获取所的顺序是乱序的,超乎我们控制的。上面例子最理想的情况就是把业务逻辑抽离出来,把获取锁的代码放在一个公共的方法里面,让这两个线程获取锁都是从我的公共的方法里面获取。

当Thread1线程进入公共方法时,获取了A锁,另外Thread2又进来了,但是A锁已经被Thread1线程获取了,所以只能阻塞等待。Thread1接着又获取锁B,Thread2线程就不能再获取不到了锁A,更别说再去获取锁B了,这样就有一定的顺序了。只有当线程1释放了所有锁,线程B才能获取。

修改后的例子:

import java.util.concurrent.TimeUnit;

public class DeadLock2 {

    private static Object lockA = new Object();
    private static Object lockB = new Object();

    public void deadLock() {
        Thread threadA = new Thread(() -> {
            getLock();
        });
        Thread threadB = new Thread(() -> {
            getLock();
        });
        threadA.start();
        threadB.start();
    }

    private void getLock() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + "获取 lockA 成功");
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "尝试获取 lockB");
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "获取 lockB 成功");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        DeadLock2 deadLock2 = new DeadLock2();
        deadLock2.deadLock();
    }
}
deadlock_07.png

2)超时放弃

当线程获取锁超时了则放弃,这样就避免了出现死锁获取的情况。当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。

总结:

死锁就是“两个任务以不合理的顺序互相争夺资源”造成,因此为了规避死锁,应用程序需要妥善处理资源获取的顺序。 另外有些时候,死锁并不会马上在应用程序中体现出来,在通常情况下,都是应用在生产环境运行了一段时间后,才开始慢慢显现出来,在实际测试过程中,由于死锁的隐蔽性,很难在测试过程中及时发现死锁的存在,而且在生产环境中应用出现了死锁,往往都是在应用状况最糟糕的时候——在高负载情况下。因此,开发者在开发过程中要谨慎分析每个系统资源的使用情况,合理规避死锁。

相关文章

  • Java 如何快速排查死锁?

    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。 (1)实例 死锁的...

  • 如何快速排查死锁?如何避免死锁?

    前言 相信程序员都会碰上这样的问题,Java死锁如何排查?又如何解决呢?那么,何为死锁呢?死锁是指两个或两个以上的...

  • java死锁排查

    锁是个非常有用的工具,运用场景非常多,因为其使用起来非常简单,而且易于理解。但同时它也会带来一些困扰,那就是可能会...

  • java死锁排查

    多个线程在竞争锁的过程中彼此之间形成堵塞的现象 排查 jstack查看线程以及堆栈信息 jconsole可视化工具...

  • java并发--java死锁

    本篇结构: 前言 什么是死锁 产生死锁的必要条件 死锁的代码示例 死锁排查 如何避免死锁 总结 一、前言 今天被问...

  • jstack命令:教你如何排查多线程问题

    这是之前的一个死锁案例: 一个多线程死锁案例,如何避免及解决死锁问题? 如程序中发生这样的死锁问题该如何排查呢?我...

  • java死锁问题排查

    首先熟悉一下jstack命令的用法,主要参数有-F -l -m 如下图: 模拟一段死锁的java代码,如下: ``...

  • Java相关的性能调优方案

    本文介绍了在性能测试过程中Java进程消耗CPU过高的问题排查方法、线程死锁问题排查方法和内存泄露的排查方法 Ja...

  • Java--死锁以及死锁的排查

    最近遇到了死锁的问题,所以这里分析并总结下死锁,给出一套排查解决方案。 死锁示例一 清单一 清单一代码有点长,但是...

  • Java程序死锁,3种方式快速找到死锁代码

    java程序中出现死锁问题,如果不了解排查方法,是束手无策的,今天咱们用三种方法找到死锁问题。 运行下面代码 程序...

网友评论

    本文标题:Java 如何快速排查死锁?

    本文链接:https://www.haomeiwen.com/subject/dnuqcktx.html