一.死锁是什么?
死锁指两个或者两个以上的线程在执行过程中,去争夺同样一个共享资源,造成的相互等待的现象,如果没有外部干预,线程会一直阻塞,无法往下执行,这样一直处于相互等待资源的线程叫做死锁线程。
二.为什么会有死锁?(死锁发生的条件)
同时满足四个条件就会产生死锁。
(1)互斥条件:一个资源每次只能被一个线程使用。
(2)请求与保持条件:一个线程因为请求资源而阻塞时,对已经获得的资源保持不放。
(3)不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺。
(4)循环等待条件:若干线程之间形成一个头尾相接的循环等待资源的关系。
三.如何解决死锁问题
死锁发生后,只能通过人工干预来解决,比如重启服务或者杀死这个线程。所以只能在编码时避开可能出现死锁的问题,而按照死锁发生的四个条件,只用破坏其中的一种就能避免死锁的发生。其中互斥条件没有办法被破坏,因为互斥条件是互斥锁的基本约束,其他的三个条件都有办法破坏。
对于请求和保持条件:一次性申请所有的资源,就不存在锁等待。
对于不可剥夺条件:占用资源的线程在进一步申请其他资源的时候,如果申请不到,可以主动去释放它占有的资源。
对于循环等待条件:可以按序申请来预防,线程在申请资源时按照顺序资源,线性化来避免循环。
四.Java中简单的死锁demo
// a对象
public static Object a = new Object();
// b对象
public static Object b = new Object();
public static void main(String[] args) {
new Thread(() -> {
// 获取对象a的锁
System.out.println(Thread.currentThread().getName() + "请求" + a + "的锁");
synchronized (a) {
System.out.println(Thread.currentThread().getName() + "获取到" + a + "的锁");
// 线程等待2s
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取对象b的锁
System.out.println(Thread.currentThread().getName() + "请求" + b + "的锁");
synchronized (b) {
System.out.println(Thread.currentThread().getName() + "获取到" + b + "的锁");
}
}
}).start();
new Thread(() -> {
// 获取对象b的锁
System.out.println(Thread.currentThread().getName() + "请求" + b + "的锁");
synchronized (b) {
System.out.println(Thread.currentThread().getName() + "获取到" + b + "的锁");
// 线程等待2s
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取对象a的锁
System.out.println(Thread.currentThread().getName() + "请求" + a + "的锁");
synchronized (a) {
System.out.println(Thread.currentThread().getName() + "获取到" + a + "的锁");
}
}
}).start();
}
五.如何排查死锁问题
方法一:通过jps命令找到运行的Java线程id,再通过jstack命令查看线程是否有死锁
方法二: 通过jps命令找到运行的Java线程id,再通过jcmd命令查看线程是否有死锁
方法三:使用Java内置性能分析工具jconsole
jps:列出正在运行的虚拟机进程。
jcmd:列出当前运行的所有虚拟机。
jstack:生成当前时刻的虚拟机线程快照。
jmap:生成堆转储快照。