死锁

死锁

       死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,若无外力作用,这些进程都将无法再向前推进。

1 产生死锁的原因

1.1 竞争资源

       当系统中供多个进程共享的资源不足时,将引起进程对资源的竞争而产生死锁。

       竞争资源引起的死锁,竞争的资源可以分为可剥夺性资源(某进程在获得这类资源后,该资源可以被其他进程或系统剥夺,如处理机和内存资源)和不可剥夺性资源(当某进程获得这类资源后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等)。

  1. 竞争非剥夺性资源引起的死锁,在系统中竞争非剥夺性资源,由于它们的数量不能满足诸进程运行的需要,会使进程在运行过程中,因争夺这些资源而陷入僵局。
  2. 竞争临时性资源而引起的死锁,对于打印机这类可以重复使用的资源成为永久性资源,而临时性资源则是由一个进程产生,被另一个进程使用短暂时间后便无用的资源,其也可能会引起死锁。

1.2 进程推进顺序非法

       进程在运行过程中具有异步性特征,如果它们之间的推进顺序不当,也同样会导致进程产生死锁。

2 产生死锁的条件

2.1 互斥条件

       进程所竞争的资源必须被互斥使用。在一段时间内某资源只由一个进程占用,如果此时还有其他进程请求该资源,则请求者只能等待,直至占有该资源的进程用完释放。

2.2 请求与保持条件

       当前已拥有资源的进程,仍能申请新的资源;而且,当该进程因新的资源被其他进程占用而阻塞时,它对自己已获得的资源仍保持不放。

2.3 不可剥夺条件

       进程已获得的资源,只能在使用完成时自行释放,而不能被抢占。

2.4 循环等待条件

       存在一个至少包含两个进程的循环等待链,链中的每个进程都正在等待下一个进程所占有的资源。

3 处理死锁的基本方法

3.1 预防死锁

       预防死锁是通过设置某些限制条件,破坏死锁的某个必要条件来防止死锁的发生。四个必要条件中,后三个条件都可被破坏,而第一个条件,则应加以保持。但由于所施加的限制条件往往太严格,因而导致系统资源利用率和系统吞吐量降低。

1)摒弃请求与保持条件

       规定所有进程在开始运行之前,都必须一次性地申请其在运行过程中所需的全部资源。若系统有足够的资源,则可分配给进程,这样进程在运行期间都不会提出资源请求,从而摒弃了请求条件,若系统有一种资源无法满足某进程的要求时,即使其他资源都空闲,也不分配给该进程,而让其等待,在等待中不会占用任何资源,也摒弃了请求和保持条件。

       优点在于简单、易于实现和安全;缺点为资源严重浪费,降低了资源利用率,进程不能保证及时运行。

2)摒弃不可剥夺条件

       规定一个已经保持了某些资源的进程,在提出新的资源请求而不能立即得到满足时,必须释放它已获得的所有资源。该方法实现起来比较复杂,且要付出很大代价,因为一个资源在使用一段时间后被释放,可能会造成前一阶段工作的失效;而反复地申请和释放资源,又会使进程的执行无限推迟,从而进一步增加系统的开销,降低系统的吞吐量。

3)摒弃循环等待条件

       将系统中的资源按类型赋予不同的序号,并规定所有的进程必须严格按照资源序号递增的顺序申请资源,这样占据较低序号资源的进程可能等待占据较高序号资源的进程释放资源,但不可能存在反向的等待,因此,不会形成进程的循环等待链。这种策略的资源利用率和系统吞吐量都比前两种方式有显著的提高,其主要问题是:为方便用户编程,资源的编号必须相对稳定,这就限制了新设备类型的增加;另外,尽管在为资源类型分配序号时,已考虑到大多数进程实际使用这些资源的顺序,但仍有很多进程使用资源的顺序与系统规定的顺序不同,从而造成资源的浪费。

3.2 避免死锁

事先预防的策略,把系统的状态分为安全状态和不安全状态,在资源的动态分配过程中,用某种方法使系统始终都处于安全状态,避免死锁的发生。

3.2.1 系统的安全状态与不安全状态

       所谓安全状态,是指系统能按某种进程顺序如<P1,P2,…,Pn>(称< P1,P2,…,Pn >序列为安全序列),来为每个进程分配其所需资源,直至最大需求,使每个进程都可顺利地完成。若系统不存在这样一个安全序列,则称为处于不安全状态。

       虽然并非所有不安全状态都将导致死锁状态,但当系统进入不安全状态后,便可能进而进入死锁状态;反之,只要系统处于安全状态,系统便可避免进入死锁状态。因此,在避免死锁的方法中,允许进程动态地申请资源,并在每次分配资源时进行安全性检查。若此次分配不会导致系统进入不安全状态,便将资源分配给进程;否则,进程将进入等待状态。

       避免死锁的实质在于,系统在进行资源分配时,如何使系统不进入不安全状态。

3.2.2 银行家算法

1. 银行家算法的数据结构

       ① 可利用资源向量Available,这是一个含有m个元素的数组,其中每一个元素代表一类可利用的资源数目,初始值为系统所配置的改类全部可用资源的数目,Available[j] = k,表示系统中Rj资源有k个。

  ② 最大需求矩阵Max,这是一个n * m 的矩阵,定义了系统中n个进程中的每一个进程对m类资源的最大需求,Max[i,j] = k,表示进程i需要Rj类资源的最大数目为k。

  ③ 分配矩阵Allocation,是一个n * m 的矩阵,定义了系统中每一类资源当前已经分配每一进程的资源数,Allocation[i,j] = k,表示进程i已经分得Rj类资源的数目为k。

  ④ 需求矩阵Need,是一个n * m的矩阵,表示每一个进程尚需的各类资源数。Need[i,j] = k,表示进程i还需要Rj类资源k个。

  Need[i, j] = Max[i, j] - Allocation[i, j]

2. 银行家算法思想

       设Requesti进程Pi的请求向量,若Requesti[j] = K,表示进程Pi需要Rj类资源K个,当Pi发出资源请求后,系统按照下述步骤进行检查

  ① 如果Requesti[j] <= Need[i, j],转向②,否则,因为它所需要的资源数已超过它所宣布的最大值,出错。

  ② 如果Requesti[j] <= Available[j],转向③,否则,表示尚无足够资源,Pi需要等待。

  ③ 系统尝试把资源分配给进程Pi,并修改数据结构中的数值:

  Available[j] = Available[j] - Requesti[j];

  Allocation[i, j] = Allocation[i, j] + Requesti[j];

  Need[i, j] = Need[i, j] - Requesti[j];

  ④ 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态,若安全,才正式将资源分配给进程Pi,以完成本次分配,否则,将本次的尝试分配作废,恢复原来的资源分配状态,让进程Pi等待。

3. 安全性算法

       系统所执行的安全性算法可描述如下

  ① 设置工作向量Work,表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work = Available;

  ② 设置Finish,表示系统是否有足够的资源分配给进程,使之运行完成,开始时Finish[i] = false,当有足够资源分配给进程时,再令Finish[i] = true;

  ③ 从进程集合中找到一个能满足下述条件的的进程:Finish[i] = fasle;Need[i, j] <= Work[j],若找到,进入④,否则,执行⑤

  ④ 当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,执行 Work[j] = Work[j] + Allocation[i, j]; Finish[i] = true;进入步骤③

  ⑤ 如果所有的进程的Finish[i] = true都满足,则表示系统处于安全状态,否则,系统处于不安全状态。

3.2.3 区分预防死锁与避免死锁

       预防死锁和避免死锁这两种方法都是通过施加某些限制条件,来预防发生死锁,两者主要差别在于:为预防死锁所施加的限制条件较严格,往往会影响进程的并发执行,而避免死锁所施加的限制条件相对宽松,这个进程的运行提供了较宽松的环境,有利于进程的并发执行。

       预防死锁就是在运行之前,预先防止死锁的产生,这主要是通过破坏产生死锁的四个必要条件中任何一个来实现的。所以系统预先确定一些资源分配策略,进程按规定申请资源,系统按预先规定的策略进行分配,从而防止死锁的发生。

       避免死锁就是在系统运行过程中注意避免死锁的发生,这就要求系统每当在进程申请资源时,都应根据一定的算法判断,仅当系统处于安全状态时才把资源分配给进程,使系统一直处于安全状态之中,从而避免死锁。

       死锁的避免比起死锁的防止策略系统资源的利用率更高一些。

3.3 检测死锁

3.3.1 什么是死锁检测

       如果系统对资源的分配不加限制,则系统可定时运行一个检查程序,来检测系统中是否存在死锁,当检测到死锁时再设法解除死锁。这种处理方式称为死锁检测。完成死锁检测任务的程序称为死锁检测程序。

3.3.2 资源分配图

       资源分配图是描述进程和资源间申请与分配关系的一个有向图。图中的结点可分为进程和资源两类,通常用圆圈代表一个进程结点,方框代表一个资源结点。由于一类资源可能有多个,因此常用方框中的一个点表示其中的一个资源。图中的边也可分为分配边和请求边两类,分配边由资源结点指向进程结点,表示相应的一个资源已被分配给对应的进程;请求边则是由进程结点指向资源结点,表示相应进程申请一个对应的资源。

既没有请求边也没有分配边与之相连的进程结点称作孤立节点。将有请求边,但目前可用资源无法满足其要求的进程结点称作阻塞结点。

3.3.3 死锁的检测

 

       利用资源分配图可以检测系统中是否存在死锁进程,判定死锁的法则如下:

  1. 如果资源分配图中无环路,则此时系统内不存在死锁。
  2. 如果进程分配图中有环路,且处于环路内的每类资源均只有一个,则此时系统内存在死锁;处于环路内的进程就是卷入死锁的进程。
  3. 如果资源分配图中有环路,但处于环路内的每类资源的个数不全为一个,则此时系统内是否存在死锁,还要化简资源分配图才能判定。

进程资源分配图的化简过程:

(1)找出一个既不阻塞又非孤立的进程结点Pi,Pi进程对于Rj资源的请求边和资源Rj对其它进程Pk的分配边之和应小于或等于其总的资源总数Wj。在这种情况下,Pi可获得它需要的全部资源Rj,并能运行推进到终点,这样就可以删除与Pi直接相连的所有请求边和分配边,使其成为孤立点。

(2)进程Pi所占有的资源全部释放时,可唤醒等待此资源而进入阻塞的进程Pj,使原来处于阻塞的进程可能变成非阻塞进程,由于其资源能满足要求,它由始点运行到终点,最后释放全部资源,使Pj也变成孤立点。

(3)若资源分配图通过上述简化步骤后,都成为孤立点,则该图是可完全化简的,并且该进程资源分配图所代表的系统状态是安全的;若进程资源分配图不能通过任何进程予以化简或者化简以后仍存在着若干个边(请求边或分配边)的话,则称该图为不可化简的。

3.3.4 死锁定理

       可通过化简资源分配图的办法来检测系统状态S是否为死锁状态。有关文献证明所有的化简顺序将得到相同的结果。S状态为死锁状态的充分条件是:当且仅当S状态的资源分配图是不可完全化简的。该条件被称为死锁定理。

3.4 解除死锁

       当发现有进程死锁时,便应立即将它们从死锁状态中解脱出来。常用的两种方法是:

1)剥夺资源

       从其他进程剥夺足够数量的资源给死锁进程,以结束死锁状态。

2)撤销进程

       最简单和最常用的方法是撤销全部死锁进程。也可以按某种顺序逐个地撤销死锁进程,直至有足够的资源可用,使死锁状态清除为止。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
死锁是指两个或多个进程在互斥地请求资源的时候,由于资源被占用而无法继续执行,导致所有进程都被阻塞的情况。在Spring Boot中,死锁可能发生在多线程并发访问共享资源时,例如数据库连接池、缓存、锁等。 在给出解决方案之前,我们需要先排查死锁问题。一种常用的排查死锁问题的方法是使用jstack命令输出线程的堆栈信息。通过查看堆栈信息,我们可以定位到可能引起死锁的代码行,并进行解决修复。 下面是一个示例代码,模拟了一个可能导致死锁的场景: ```java package com.xz.springboottest.day1; public class DeadLock { private static final String ACTION_ONE = "拿起碗"; private static final String ACTION_TWO = "拿起筷子"; public static void main(String[] args) { // 哲学家小明 new Thread(() -> { synchronized (ACTION_ONE) { try { Thread.sleep(1000); synchronized (ACTION_TWO) { System.out.println("小明开始吃饭了。。。。。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); // 哲学家小张 new Thread(() -> { synchronized (ACTION_TWO) { try { Thread.sleep(1000); synchronized (ACTION_ONE) { System.out.println("小张开始吃饭了。。。。。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } ``` 以上代码模拟了哲学家就餐的场景,当只剩下一只碗和一双筷子时,可能出现死锁的问题。 对于解决死锁的方法,可以考虑以下几种方案: 1. 避免循环等待:为了避免死锁,可以规定所有线程在请求资源时按照固定的顺序获取,从而避免循环等待。 2. 加锁顺序:在多个线程同时请求多个资源的情况下,为了避免死锁,可以约定线程必须按照相同的顺序请求资源。 3. 设置超时时间:在获取锁的时候设置超时时间,如果超过一定时间还未获取到锁,可以放弃或者重试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值