死锁是什么?
死锁是🈯️两个或两个以上的进程在执行过程中,因争抢资源而造成的一种互相等待现象 ,在无外力干涉时,永远无法推进下去。如果系统资源充足,进程的资源请求都能得到满足,死锁出现的可能性很低,否则就会因争夺有限的资源而陷入死锁。
产生死锁的主要原因:系统资源不足,进程运行推进的顺序不合适,资源分配不当。
死锁产生的四个条件以及预防
条件
- 互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
- 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
- 循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所申请的资源。
预防
- 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
- 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
- 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。
死锁Demo
class HoldThread implements Runnable{
private String lockA;
private String lockB;
public HoldThread(String lockA,String lockB){
this.lockA=lockA;
this.lockB=lockB;
}
@Override
public void run(){
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockA+"\t 尝试获得:"+lockB);
try{TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e){e.printStackTrace();}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockB+"\t 尝试获得:"+lockA);
}
}
}
}
public class Demo {
public static void main(String[] args) throws Exception {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldThread(lockA,lockB),"Thread1").start();
new Thread(new HoldThread(lockB,lockA),"Thread2").start();
}
}
运行结果:
证明及解决方法
在linux系统下,查看进程用 ps -ef |grep,在jdk中有个jps,相当于java版的ps。
1.首先通过 jps -l,查看Demo.class文件被哪个线程执行,如图是进程编号为2529的线程
2.第二步,用jstack ${进程编号} 查看进程的错误信息
错误信息里写着,Thread1和2 lock了自己的资源,wait着对方的资源。