什么是死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。
- 互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
- 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
- 循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。
预防死锁
我们可以通过破坏死锁产生的4个必要条件来 预防死锁,由于资源互斥是资源使用的固有特性是无法改变的。
- 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
- 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
- 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。
避免死锁
进行银行家算法检查算法判断是否处于安全状态,安全状态继续运行,不安全状态拒绝资源请求分配,然后继续执行。
Java模拟死锁
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new DeadLock(lockA, lockB),"Thread1").start();
new Thread(new DeadLock(lockB, lockA),"Thread2").start();
}
}
class DeadLock implements Runnable{
private String lockA; //锁A
private String lockB; //锁B
public DeadLock(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
// 锁A
synchronized (lockA) {
System.out.println(Thread.currentThread().getName()+" 持有:" + lockA + ",尝试获取:" + lockB);
// 休眠一下
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
// 锁B
synchronized (lockB) {
System.out.println(Thread.currentThread().getName()+" 持有:" + lockB + ",尝试获取:" + lockA);
}
}
}
}
结果
排查死锁
这是一个简单的Demo,在实际生产中情况要比这个复杂的多,而且仅靠一张照片没有说服力,得拿出证据。
Java 提供的有 jsp 和 jstack 工具,可以配合使用找出故障原因。
jps -l
jstakc 进程号
这个截图充分说明了错误产生的原因