死锁(deadlock)
-
资源抢占问题
-
什么是死锁?
-
死锁产生的必要条件?
-
如何解决死锁问题?
1、资源抢占问题
??假设进程A正持有资源S, 此时进程B也对资源S请求使用并且成功,同时进程B抢占资源后没有造成副作用,那么就可以说资源S是可抢占式资源。
??可抢占资源
假设系统内存为512MB和一台打印机。此时有进程A、进程B,这两个进程所需要的内存恰好也都是512MB。进程A首先申请到打印机,进程B申请到了512MB的内存。此时这两个进程都不会正常工作,因为进程A需要512MB的内存,该内存又恰好被进程B所占用;对于进程B虽然申请到了所需要的内存,但是没有申请到打印机,也不能正常打印。如果将进程B占用的内存换出,进程A换入内存中,那么进程A就能正常打印,之后进程B也能正常打印。这一过程中内存(储存器)就是可抢占资源。
??不可抢占资源
假设进程A正在执行光盘刻录任务,此时进程B也申请光盘刻录, 光盘刻录机此时是不会被分配给进程B的,因为这会对进程A的刻录产生损坏。这种情况下,光盘刻录机就是不可抢占资源(抢占了就会造成不好的后果)。
2、什么是死锁?
??对于可抢占资源
中的进程A和进程B而言,内存是可抢占式资源。但是如果它们抢占的不是内存,而是一种不可抢占资源,又会有怎么样的结果?也就是说进程A持有资源S,进程B持有资源T,进程A的执行还要再持有资源T,进程B的执行也需要持有资源S(S、T都是不可抢占资源,它们都被某个进程占用),这就造成进程A与进程B均处在阻塞状态。这种情况就是死锁。此时进程A、进程B和资源S、资源T之间的示意图如下所示
3、死锁产生的必要条件?
?(1)互斥条件: 在某一段时间内,当进程A持有资源S时,进程A是排它性的占用该资源,也就说在进程A占用资源S的期间内,别的进程申请资源S只能先等待进程A释放资源S。
?(2)请求和保持条件: 进程A在已占用资源S的情况下,进程A还可以向别的资源发出申请,但依然占用资源S。
?(3)不可抢占条件: 在进程A未释放资源S期间内,资源S是一直被进程A所占用的,别的进程不能抢占,只能先等待进程A显式释放资源S。
?(4)环路等待条件: 当发生死锁时,系统内至少有2个进程形成了资源环路(类似死锁示意图所示)。在这个环内,任意一个进程\(P_{i}\)都在等待下一个进程\(P_{i+1}\)释放\(P_{i}\)需要的资源程\(S_{i}\)。
4、如何解决死锁问题?
?(1)鸵鸟算法(忽视策略): 鸵鸟算法意思是直接忽略死锁问题。
?(2)死锁的检测与恢复(检测与恢复策略): 首先允许发生死锁问题,然后通过相关检测算法对死锁进行检测;当检测出现的死锁后,利用恢复技术进行恢复。相关的恢复方法有
?(3)分配资源前进行仔细的检查避免死锁(避免策略): 相关的检查方法有银行家算法,分配资源前先检测资源分配之后是否导致不安全的状态。缺点是银行家算法需要知道资源分配的数值,但资源值以及进程数一般都是运行时态才知道的。
?(4)破坏死锁的四个条件之一(预防策略):
???① 破坏互斥条件:破坏进程对某个资源的排它性占有。
???② 破坏请求和保持条件:直接分配给该进程所需的全部资源供其使用。其实如果事先知道了所需的全部资源是哪些,银行家算法也可以使用。
???③ 破坏抢占条件:有些资源可被虚拟化,然后就可以抢占被持有的资源且不发生会乱。
???④ 破坏环路条件:资源进行编号,严格按号来调度资源。
5、Java死锁的例子
public class DeadLock {
public static void main(String[] args) {
String res1 = "resource_1";
String res2 = "resource_2";
new Thread(new MyThread(res1, res2), "t1").start();
new Thread(new MyThread(res2, res1), "t2").start();
}
}
class MyThread implements Runnable {
String res1;
String res2;
public MyThread(String res1, String res2) {
this.res1 = res1;
this.res2 = res2;
}
@Override
public void run() {
// 首先获取一个资源
synchronized (res1) {
System.out.printf("%s: %s, wangting --> %s\n", Thread.currentThread().getName(), res1, res2);
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
// 再申请另一个资源
synchronized (res2) {
System.out.printf("%s: %s, wangting --> %s\n", Thread.currentThread().getName(), res2, res1);
}
}
}
}