回答
死锁指的是两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果没有外力的帮助下,它们都将无法推进下去。简单的说就是两个或以上的进程都在互相等待对方释放资源,但是它们谁都不释放进程,系统就卡住了。
解决死锁的方法主要有以下几种:
- 以确定的顺序获得锁:在获取锁的顺序上进行控制,确保所有线程获取锁的顺序是一致的,这样就可以预防死锁的产生。
- 超时放弃:在获取锁的时候设置超时时间,如果在指定时间内未获取锁,我们可以做其他操作,避免死锁。比如使用
ReentrantLock
的tryLock()
。 - 监控和检测:使用一些死锁检测工具来检测线程是否发生了死锁,如果发现死锁,我们可以采取一些必要的措施,比如中断其中一个线程。
- 避免嵌套锁:尽量避免一个线程同时持有多把锁
扩展
死锁演示
产生死锁有四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 占有并等待条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺,只能由该进程自己释放。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
举一个例子:
public class DeadlockTest {
private static final Object resourceA = new Object();
private static final Object resourceB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resourceA) {
System.out.println(Thread.currentThread().getName() + "获取【资源 A】");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (resourceB) {
System.out.println(Thread.currentThread().getName() + "获取【资源 B】");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resourceB) {
System.out.println(Thread.currentThread().getName() + "获取【资源 B】");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA) {
System.out.println(Thread.currentThread().getName() + "获取【资源 A】");
}
}
});
thread1.start();
thread2.start();
}
}
Thread-0
先锁住 resourceA
,然后等待 resourceB
,Thread-1
先锁住 resource
B,然后等待 resourceA
,两个线程都在等待对方释放锁,然后他们就进入死锁状态了。
死锁排查
排查死锁的方式有几种,大明哥一般都是直接使用 jstack 命令。
使用 jstack 命令排查死锁
首先,利用 jps
拿到当前 Java 应用程序的 pid
。
使用命令 jstack -l 42242
,我们可以得到如下信息:
这图以一种非常清晰的方式告诉你,Thread-0
在等待哪个锁资源,这个资源被哪个线程持有,Thread-1
在等待哪个锁资源,这个资源被哪个线程持有。一目了然!!
当然,我们还可以使用其他的方式来检测、查看死锁,比如Java VisualVM
,不过在实际应用过程中,jstack
完全够用了。