龙空技术网

面试三连:什么是死锁?怎么检测?怎么预防?

尚硅谷教育 182

前言:

此时我们对“简述死锁的定义”大约比较讲究,大家都想要知道一些“简述死锁的定义”的相关知识。那么小编也在网络上收集了一些有关“简述死锁的定义””的相关资讯,希望小伙伴们能喜欢,咱们一起来学习一下吧!

在Java面试过程中,死锁也是高频考察点之一,如果线上环境出现了死锁问题,估计要拿出一位程序员去祭天才行了。

在进行多线程编程中,我们为了防止多个线程同时对一个共享资源进行操作,一般都会在操作这个共享资源之前加上互斥锁,只有成功获取到锁的线程,才能够操作这个共享资源。但是在一些特定条件下,也会产生一些问题,比如死锁,接下来我们就聊聊死锁的问题。

1. 死锁的定义

在 Java 中,死锁(Deadlock)情况是指:两个或两个以上的线程持有不同系统资源的锁,线程彼此都等待获取对方的锁来完成自己的任务,但是没有让出自己持有的锁,线程就会无休止等待下去。线程竞争的资源可以是:锁、网络连接、通知事件,磁盘、带宽,以及一切可以被称作“资源”的东西

概念性的东西读起来可能有点抽象,那我举个比较容易理解的例子:假设有两个线程A 和 B,两把锁 LockA和LockB,线程A持有LockA锁,线程B持有LockB锁 , 在双方不释放锁的情况下,再尝试去获取对方持有的锁,也就是说,线程A 手里拿着LockA锁不放,又去获取LockB锁(此时LockB锁在线程B手里),而线程B手里拿着LockB锁不放,又去获取LockA锁(此时LockA锁在线程A手里),在这种情况下,就会陷入无限等待的死锁状态。

代码示例:

public class DeadLockDemo {

//两把锁 LockA 和 LockB

private static Object lockA = new Object();

private static Object lockB = new Object();

public static void main(String[] args) {

//开启两个线程 A B

//线程A

new Thread(()->{

//线程A获取到LockA锁

synchronized (lockA){

System.out.println("线程A获取到LockA锁");

try {

//睡眠100毫秒,确保B线程获取到LockB锁

Thread.sleep(100);

//尝试获取lockB,此时lockB在线程B手里没有释放

synchronized (lockB){

System.out.println("线程A获取到LockB锁");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"A").start();

//线程B

new Thread(()->{

//线程B获取到LockB锁

synchronized (lockB){

System.out.println("线程B获取到LockB锁");

try {

//睡眠100毫秒,确保A线程获取到LockA锁

Thread.sleep(100);

//尝试获取lockA,此时lockA在线程A手里没有释放

synchronized (lockA){

System.out.println("线程B获取到LockA锁");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"B").start();

}

}

输出:

线程A获取到LockA锁

线程B获取到LockB锁

2.如何检测和排查死锁

我们知道了什么是死锁,下一步就是当遇到死锁问题时,如何利用工具去排查和检测死锁问题,下面我们就介绍两款好用的工具来帮助我们检测死锁。

2.1 使用Jstack 工具

JDK中自带的 jstack 工具是一个线程堆栈分析工具,可以帮助我们排查Java程序中是否有死锁问题。

首先我们在命令行输入jps查看当前正在执行任务的PID

atguigu@Zhang JavaSE % jps

# 以下显示的是进程名称和对应的PID号

44080 Launcher

44432 DeadLockDemo

29560

44094 Jps

可以看到任务进程的 PID 为 44432,然后我们使用 jstack工具查看当前进程的堆栈信息

atguigu@Zhang JavaSE % jstack 44432

# 以下显示的是进程的堆栈信息

Found one Java-level deadlock:

=============================

"B":

waiting to lock monitor 0x000000014980eec0 (object 0x000000076ac71460, a java.lang.Object),

which is held by "A"

"A":

waiting to lock monitor 0x0000000149810360 (object 0x000000076ac71470, a java.lang.Object),

which is held by "B"

Java stack information for the threads listed above:

===================================================

"B":

at com.atguigu.thread.DeadLockDemo.lambda$main$1(DeadLockDemo.java:42)

- waiting to lock <0x000000076ac71460> (a java.lang.Object)

- locked <0x000000076ac71470> (a java.lang.Object)

at com.atguigu.thread.DeadLockDemo$$Lambda$2/1791741888.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"A":

at com.atguigu.thread.DeadLockDemo.lambda$main$0(DeadLockDemo.java:23)

- waiting to lock <0x000000076ac71470> (a java.lang.Object)

- locked <0x000000076ac71460> (a java.lang.Object)

at com.atguigu.thread.DeadLockDemo$$Lambda$1/1329552164.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

2.2 使用Jconsole 工具

Jconsole是JDK自带的监控工具。它用于连接正在运行的本地或者远程的JVM,对正在运行的Java应用程序的资源消耗和性能进行监控,提供强大的可视化界面,本身占用的服务器内存很小,甚至可以说几乎不消耗。

# 命令行输入 jconsole 指令

jconsole

弹出图形化界面

可以看到以下结果

可以看到进程中是存在死锁的 。

3.如何避免产生死锁

其实产生死锁也不是那么容易的,需要满足四个条件,才会产生死锁。

互斥,也就是多个线程在同一时间使用的不是同一个资源。持有并等待,持有当前的锁,并等待获取另一把锁不可剥夺,当前持有的锁不会被释放环路等待,就是两个线程互相尝试获取对方持有的锁,并且当前自己持有的锁不会释放。

我们只需要让其中一个条件不成立,那么就可以避免死锁问题的产生。一般最常见的解决方式就是使用资源有序分配法,来使环路等待条件不成立。

环路等待就是我们刚开始代码演示的那种情况,两个线程互相尝试获取对方的锁,但是他们两个都不会释放自己的锁,这样就会陷入一个无限循环等待,这种情况就是环路等待。

资源有序分配法其实很简单,就是把线程获取资源的顺序调整为一致的即可,资源可以理解为代码示例中的锁。

那么我们只需要修改线程A或者线程B的代码即可。线程B原本是先获取LockB再获取LockA,改成和线程A一样的获取顺序,先获取LockA,再获取LockB:

//线程B 获取锁的顺序和线程A保持一致

new Thread(()->{

//线程B第一次也获取LockA 锁

synchronized (lockA){

System.out.println("线程B获取到LockA锁");

try {

//睡眠100毫秒,确保A线程获取到LockA锁

Thread.sleep(100);

//第二次也获取LockB锁

synchronized (lockB){

System.out.println("线程B获取到LockB锁");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"B").start();

输出结果:

线程A获取到LockA锁

线程A获取到LockB锁

线程B获取到LockA锁

线程B获取到LockB锁

总结

简单来说,死锁就是多个线程并行执行的时候,抢夺资源而互相等待造成的。只有满足四个条件的时候才会发生。避免死锁问题只需要破坏其中一个条件即可。

标签: #简述死锁的定义