死锁的概念
死锁是指在并发系统中,两个或多个进程无限期地等待对方持有的资源,导致所有进程无法继续执行的一种状态。在死锁发生时,每个进程都在等待其他进程释放资源,而这些资源却无法被释放,造成了一个相互等待的循环。这种情况下,系统无法进一步发展,进程无法继续执行,需要人工干预才能解决问题。
死锁只有在同时满足四个条件才会发生:
- 互斥条件
- 请求与保持条件
- 不可剥夺条件
- 循环等待条件
产生死锁的四大条件
互斥条件
在一段时间内某些资源只能被一个线程占用,若还有其他线程请求资源,请求者只能等待直到占有资源的线程使用完毕释放。
比如线程A获得了资源1,此时线程B想要获取资源1,但是资源1已经被线程A占有了,那么线程B就只能等待线程A使用完以后才能获取资源1。
请求与保持条件
线程已经保持至少一个资源,但又提出了新的资源请求,而该资源已经被其他的线程所占用,此时请求线程发生阻塞,但又对自己所占用的资源保持不放。
比如线程A获得了资源1,线程B获得了资源2,此时线程A想要获取资源2,但是会进入阻塞,因为资源2已经被线程B获得了,在这个等待过程中线程A并不会释放自己的资源1。
不可剥夺条件
线程已获得的资源,在没有使用完不能被剥夺,只能在使用完后自己释放。
比如线程A获得了资源1,在没有使用完的情况下是不会被其他想要获取资源1的线程释放的,其他想要获取资源1的线程只能等到线程A使用完资源1后自动释放再去获取资源1。
循环等待条件
多个进程之间形成一种循环等待资源的关系。
比如线程A获得了资源1,线程B获得了资源2,此时线程A想要请求资源2,而线程B想要请求资源1,此时就形成了一种循环等待的条件。
如何避免死锁
- 预防死锁:设计阶段就采取措施,破坏死锁的发生条件。比如,只要破坏其中一个条件,如破坏循环等待条件,就可以避免死锁的发生。
- 避免死锁:通过资源分配策略,避免系统陷入不可解的死锁状态。例如,银行家算法可以根据资源需求的安全性进行资源分配,保证系统不会进入死锁状态。
- 检测死锁:这种方法无需事先采取任何限制性措施,允许进程在运行过程中发生死锁。但可通过检测机构及时的检测出死锁的发生,然后采取适当措施,把进程从死锁中解脱出来。
- 解除死锁:当检测到系统中已发生死锁时,就采取相应措施,将进程从死锁状态中解脱出来。常用的方法是撤销一些进程,回收它们的资源,将它们分配给已处于阻塞状态的进程,使其能继续运行。
预防死锁
预防死锁就是破坏产生死锁四大条件中的一个或者多个,避免发生死锁,但其中的互斥条件是非共享设备所必需的条件,因此不能破坏,所有破坏的主要是其余三个。
破坏请求与保持条件
当一个进程在请求资源时,不能持有不可抢占的资源。
第一种实现方式
所有进程在开始运行之前,必须一次性地申请其在整个过程中所需的全部资源。此时若系统有足够的资源分配给某进程,便可把其所需的所有资源分配给它。这样,该进程在整个运行期间,遍不会再提出资源要求,从而破坏了“请求”条件。系统在分配资源时,只要有一种资源不能满足进程的要求,即使其他所需的各资源都空闲也不分配给该进程,而让该进程等待。由于该进程在等待期间未占有任何资源,于是破坏了“保持”条件,从而可以预防死锁的发生。
这种方式的缺点极为明显,就是资源严重浪费,降低了资源利用率,进程在开始运行时就一次性地占用了整个运行过程所需的全部资源,其中有些资源可能仅在运行初期或运行快结束时才使用,甚至根本不使用。
第二种实现方式
该实现是对第一种实现的改进,它允许一个进程只获得运行初期所需的资源后,便开始运行。进程运行过程中再逐步释放已分配给自己的、且已用毕的全部资源。然后再请求新的所需资源。
破坏不可抢占条件
当一个已经保持了某些不可被抢占资源的进程,提出新的资源请求而不能得到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。该方法实现起来比较复杂,且需付出很大的代价。
破坏循环等待条件
通过对资源进行排序或者限制资源请求的顺序,可以避免形成循环等待。
可以采用顺序资源分配法,给系统中的资源编号,规定每个进程必须按照编号递增的方式请求资源,编号相同的资源(同类资源)一次申请完。
原理:一个进程只有已占有小编号的资源时,才有资格申请大编号的资源,已持有大编号的资源是不会去申请小编号资源的,从而就不会出现循环等待的现象。
缺点:
- 不方便增加新的设备,因为要重新分配编号。
- 进程实际使用资源的顺序可能和编号顺序不同,造成资源浪费。
- 必须按照编号申请资源,编程麻烦。
资料参考:《死锁的概念以及如何避免死锁》