死锁:
死锁是指由于两个或多个线程互相持有对方的资源,导致这些线程处于等待状态,无法继续向下执行。
死锁产生的原因:只有满足下面四个条件才会形成死锁,缺一不可
1. 互斥条件:线程对于所分配到的资源具有排他性,即一个资源只能被一个线程持有,直到该线程释放。
2. 请求和保持条件:一个线程在请求被占用资源时,不会释放自己的资源。
3. 不剥夺条件:一个线程的资源只能由自己释放,其它线程无法剥夺。
4. 循环等待条件:所有的等待的线程必定会形成一个环路,无法自己退出。
处理死锁的方法:
一,预防死锁
通过破坏形成死锁的四个必要条件之一来防止死锁的发生。(必定不会出现死锁)
1. 破坏循环等待条件:确保所有的线程都是按照一定的顺序获得锁,那么死锁就不会发生。如果一个线程需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。但是这个方法必须事先知道所有的锁。
2. 破坏不剥夺条件:当一个已经持有资源的线程在请求一个被占有资源时,必须释放所有已经持有的资源。该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致线程之前的工作失效等,反复的申请和释放资源会导致线程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
3. 破坏请求和保持条件:所有的线程在运行之前,必须先一次性地申请其所需要的所有资源。这个方法简单易实施且安全,但因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。第二种方法是:允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。
二,避免死锁
不强制破坏四个必要条件,在系统的运行过程中避免死锁的发生。(死锁可能发生,尽力避免)
1. 在使用前进行判断,只允许不会产生死锁的进程申请资源:
银行家算法:
银行家算法是从当前状态出发,按照系统各类资源剩余量逐个检查各进程需要申请的资源量,找到一个各类资源申请量均小于等于系统剩余资源量的进程P1。然后分配给该P1进程所请求的资源,假定P1完成工作后归还其占有的所有资源,更新系统剩余资源状态并且移除进程列表中的P1,进而检查下一个能完成工作的客户,......。如果所有客户都能完成工作,则找到一个安全序列,银行家才是安全的。若找不到这样的安全序列,则当前状态不安全。
为实现银行家算法,每一个新进程在进入系统时,必须申明在运行过程中可能需要每种资源类型的最大单元数目。当进程请求一组资源时,系统必须首先确定是否有足够的资源分配给该进程。若有,再进一步计算在将这些资源分配给进程后,是否会使系统处于不安全状态。如果不会,才将资源分配给它,否则让进程等待。
假设资源P1申请资源,银行家算法先试探的分配给它(先判断当前资源池中的资源数量够不够),若申请的资源数量小于等于Available,然后判断分配给P1之后的剩余的资源,能不能使进程队列的某个进程执行完毕,若没有进程可执行完毕,则系统处于不安全状态(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁状态)。
若有进程可执行完毕,则假设回收已分配给它的资源(剩余资源数量增加),并继续根据新的Available资源判断队列中其它进程能否可执行完毕。若所有进程都可执行完毕,则系统处于安全状态,并根据可完成进程的分配顺序生成安全序列(如{P0,P3,P2,P1}表示将申请后的剩余资源Work先分配给P0–>回收(Work+已分配给P0的A0=Work)–>分配给P3–>回收(Work+A3=Work)–>分配给P2–>······满足所有进程)。
如此就可避免系统存在潜在死锁的风险。
2.死锁检测:每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。
三,检测死锁
死锁检测与恢复是指当死锁发生时,系统能够检测到死锁发生的位置和原因,并能通过外力破坏死锁发生的必要条件,从而使得并发进程从死锁状态中恢复出来。
检测死锁的方法:银行家算法中判断系统是否安全的算法。
死锁检测的频率取决于死锁发生的频率。如果死锁发生的频率高,那么死锁检测的频率也要相应提高,这样可以提高系统资源的利用率,避免更多的进程卷入死锁。如果进程申请资源不能满足也会进行死锁检测,那么每当死锁形成时即能被发现,这和死锁避免的算法相近,只是系统的开销较大。为了减小死锁检测带来的系统开销,一般采取每隔一段时间进行一次死锁检测,或者在CPU的利用率降低到某一数值时,进行死锁的检测。
四,解除死锁
如果利用死锁检测算法检测出系统已经出现了死锁 ,那么,此时就需要对系统采取相应的措施。常用的解除死锁的方法:
1. 抢占资源:从一个或多个进程中抢占足够数量的资源分配给死锁进程,以解除死锁状态。
2. 终止(或撤销)进程:终止或撤销系统中的一个或多个死锁进程,直至打破死锁状态。
终止所有的死锁进程:这种方式简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。
逐个终止进程,直至死锁状态解除:该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。另外,每次终止进程的时候终止那个进程呢?每次都应该采用最优策略来选择一个“代价最小”的进程来解除死锁状态。一般根据如下几个方面来决定终止哪个进程:
进程的优先级
进程已运行时间以及运行完成还需要的时间
进程已占用系统资源
进程运行完成还需要的资源
终止进程数目
进程是交互还是批处理