死锁就像两个人鞠躬,A说B起来我就起来,B说A起来我就起来,结果就陷入了僵局,谁也起不来。
经典的哲学家问题,五个人有六只筷子,一起拿身边的一只筷子。这样的话谁也拿不到两只筷子吃饭,但是假如有顺序的进行,那就可以破坏掉死锁。
package com.company.DeadLock;
public class DL implements Runnable {
static Object o1 = new Object();
static Object o2 = new Object();
private int flag;
public static void main(String[] args) {
DL dl1 = new DL();
DL dl2 = new DL();
dl1.flag = 1;
dl2.flag = 2;
Thread thread1 = new Thread(dl1, "t1");
Thread thread2 = new Thread(dl2, "t2");
thread1.start();
thread2.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "已启动");
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + "已上锁");
}
}
}
if (flag == 2) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + "已上锁");
}
}
}
}
}
上述案例中,就是死锁的典型案例。
我们该如何排查死锁
使用Java给我们提供的开发工具即可排查死锁、
首先启动jps,查看运行中的Java程序
即可看到DL这个程序的运行pid为54408,可以使用jstack 54408查看DL
jstack提示Found 1 DeadLock就是说明产生了死锁。
解决死锁的几个方法
首先,形成死锁需要四种条件
- 互斥条件
每个资源只能被一个线程使用,不能被多个线程或进程使用,就比如说我两个人有三个苹果,那任意拿一个都不会出现死锁,死锁的发生因为资源不够了,多个人抢占一个资源。
- 请求与保持条件
三个人抢两个游戏手柄,玩游戏需要游戏手柄,有的人玩这个手柄就是黏手,松不开(不释放资源),这样的话另个人就永远拿不到手柄
- 不剥夺条件
有的人会上去怼这个比较霸道的,有的人比较胆小,他就等,不剥夺他的手柄,这样也会形成死锁
- 循环等待
这就好比看两个人,一个人电视,一个人玩游戏,两个人互不说话,看电视的人说等游戏通关了我去玩,那边玩游戏的说等你电视看完了我去看,这样的话就永远处于僵持状态,打游戏的一直占用游戏手柄这个资源,看电视的一直占用遥控器这个资源。
在知道这些产生条件之后,我们就可以进行破坏其中一个,即可破坏死锁。
其实有时候死锁概率比较低,或者说几年就发生一次死锁的时候,鸵鸟策略也是不错的选择,就是装死,因为预期耗费大量的人力物力去解决死锁,不如把头埋在土里,装作啥也不知道。