找回密码
 立即注册
搜索
查看: 9|回复: 0

详解死锁:产生原因、条件、解决方法及预防措施

[复制链接]

1万

主题

0

回帖

5万

积分

管理员

积分
56754
发表于 14 小时前 | 显示全部楼层 |阅读模式
本文目的

本文旨在向大家详细介绍死锁,它包含死锁产生的原因,有产生的条件,还有解决方法,以及如何预防 。

死锁是什么

所谓死锁,是指在执行过程中,两个或两个以上的进程因争夺资源,造成互相等待的现象。若无外力作用,这些进程都无法推进下去。此时称系统处于死锁状态,或者说系统产生了死锁。这些永远在互相等待的进程,称为死锁进程。资源占用具有互斥性,某个进程提出资源申请后,有关进程在没有外力协助的情况下,永远无法分配到必需的资源,进而无法继续运行,这种情况产生了一种特殊现象,即死锁。

示例:

线程A持有独占锁a,此时它尝试去获取独占锁b,与此同时,线程B持有独占锁b,并且线程B尝试获取独占锁a,在这种情况下,AB两个线程会因为互相持有对方需要的锁,从而发生阻塞现象,我们将这种阻塞现象称为死锁。

<p><pre class="syl-page-code">    <code>   public class DeadLockDemo {
公共静态无效主函数,使用字符串数组参数
        // 线程a
创建一个新线程对象td1 ,该线程对象基于一个实现了Runnable接口的对象 ,此对象通过匿名内部类的方式定义 。
            public void run() {
                DeadLockDemo.method1();
            }
        });
        // 线程b
创建一个新的线程对象td2 ,该线程对象基于一个实现了Runnable接口的对象 。
            public void run() {
                DeadLockDemo.method2();
            }
        });
        td1.start();
        td2.start();
    }
公共静态无效方法1() {
        synchronized (String.class) {
            try {
                Thread.sleep(2000);
} catch (被中断异常 e) {
                e.printStackTrace();
            }
System.out.println("线程a进行尝试","去获取integer.class");
对Integer类进行同步锁定,
            }
        }
    }
公共静态无效方法2()


对整数类进行同步处理,在其作用域内执行后续代码块 。
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
系统输出打印“线程b尝试获取String.class”
            synchronized (String.class) {
            }
        }
    }
}
----------------
线程b尝试获取String.class
线程a进行尝试,去获取integer.class 。
无限阻塞下去</code></pre></p>
产生条件

进程在运行过程中,有可能发生死锁,不过死锁的发生需要具备一定条件,死锁发生必须具备以下四个必要条件 。

互斥条件是指,进程对所分配到的资源进行排他性使用。也就是说,在一段时间内,某资源只由一个进程占用。如果此时还有其他进程请求资源,那么请求者只能等待,直到占有资源的进程用毕释放。

请求和保持条件是指,进程已经保持了至少一个资源,它又提出了新的资源请求,该资源已被其他进程占有,这时请求进程阻塞,不过它对自己已获得的其他资源保持不放。

进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放,这就是不剥夺条件 。

环路等待条件是指,在发生死锁时,必然存在一个进程——资源的环形链。也就是进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源,P1正在等待P2占用的资源,以此类推,Pn正在等待已被P0占用的资源 。

产生原因

死锁产生原因主要存在两种情况,一种是由竞争资源所引发的,另一种是因进程推进顺序异常所导致的。

1,竞争资源

产生死锁中的竞争资源指的是竞争不可剥夺资源和临时资源。

可剥夺资源和不可剥夺资源

可剥夺资源是这样一种资源,某进程获得这类资源后,该资源能够被其他进程或系统剥夺。比如,优先权高的进程能剥夺优先权低的进程的处理机。又如,存储器管理程序能把一个进程从一个存储区移到另一个存储区,这会剥夺该进程原来占有的存储区,甚至还可将一进程从内存调到外存上,可见,CPU和主存均属于可剥夺性资源。

不可剥夺资源是这样一种资源,当系统把这类资源分配给某进程后,就不能强行收回,只能在进程用完后自行释放,像磁带机、打印机等就是这类资源 。

竞争不可剥夺资源

系统中配置了不可剥夺资源,这些资源的数量无法满足各个进程运行的需求,进程在运行时,会因为争夺这些资源而陷入僵局,例如,系统里仅有一台打印机R1以及一台磁带机R2,可供进程P1和P2共同使用。假设PI占用了打印机R1,P2占用了磁带机R2,要是P2继续请求打印机R1,那么P2会阻塞,要是P1又请求磁带机,P1同样会阻塞。于是,P1和P2之间形成了僵局,两个进程都在等待对方释放自己所需资源,然而它们都因无法继续获得所需资源而不能继续推进,进而也不能释放自己所占资源,最终进入死锁状态。

竞争临时资源

上面所说的打印机资源属于可顺序重复使用型资源,这种资源被称为永久资源。还有一种资源被称作临时资源,它由一个进程产生,会被另一个进程使用,在短时间后就变得无用,所以也被称为消耗性资源,像硬件中断、信号、消息、缓冲区内的消息等都属于临时资源,它也有可能引发死锁。SI、S2、S3属于临时性资源,进程P1生成消息S1,同时还需要从P3接收消息S3;进程P3产生消息S3,并且还要求从进程P2接收消息S2;进程P2产生消息S2,另外还要求从P1接收所产生的消息S1。要是消息通信依照如下顺序开展:

P1: ···(S1);(S3); ···

P2: ···(S2);(S1); ···

P3: ···(S3);(S2); ···

并不可能发生死锁。但若改成下述的运行顺序:

P1: ···(S3);(S1);···

P2: ···(S1);(S2); ···

P3: ···(S2);(S3); ···

则可能发生死锁。



2,进程推进顺序不当引起死锁

进程在运行的时候具有异步性的特征,这有可能让P1和P2这两个进程按照下述两种顺序向前推进 。

1) 进程推进顺序合法

当进程P1和P2并发执行时,如果按照下述顺序推进:

P1:(R1);

P1:(R2);

P1: (R1);

P1: (R2);

P2:(R2);

P2:(R1);

P2: (R2);

P2: (R1);

这两个进程能够顺利完成,这种推进顺序不会引起进程死锁,它是合法的。

2) 进程推进顺序非法

若P1保持了资源R1,若P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁,例如,当P1运行到P1:(R2)时,将因R2已被P2占用而阻塞,当P2运行到P2:(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁。

处理方式

系统中出现死锁后,要及时检测到死锁发生,采取适当措施解除死锁。目前处理死锁的方法可归结为以下四种:

1) 预防死锁。

这是一种事先预防的方法,它比较简单和直观。该方法是通过设置某些限制条件,以此去破坏产生死锁的四个必要条件中的一个或者几个,进而预防死锁的发生。预防死锁是一种较易实现的方法,已经被广泛使用。然而因为所施加的限制条件往往过于严格,所以可能会导致系统资源利用率降低,也可能会导致系统吞吐量降低。

资源互斥是资源使用的固有特性,这是无法改变的。所以只能通过破坏其余三个条件来预防死锁。

破坏“不可剥夺”条件:当一个进程不能获得所需的全部资源时,它便处于等待状态,在等待期间,它占有的资源会被隐式释放,重新加入到系统的资源列表中,这些资源可被其他进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源后,才可以重新启动并执行。

破坏“请求与保持”条件,有第一种方法,即静态分配,也就是每个进程在开始执行时,就申请它所需要的全部资源。还有第二种方法,即动态分配,也就是每个进程在申请所需要的资源时,它本身不占用系统资源。

破坏“循环等待”条件,采用资源有序分配。其基本思想是,将系统中的所有资源顺序编号,把紧缺的、稀少的资源采用较大的编号,申请资源时必须按照编号顺序进行,一个进程只有先获得较小编号的资源,才能申请较大编号的资源。

2) 避免死锁。

该方法属于事先预防的策略,它不须事先采取各种限制措施来破坏产生死锁的四个必要条件,而是在资源动态分配过程中,用某种方法防止系统进入不安全状态,进而避免发生死锁。

预防死锁的几种策略,会对系统性能造成严重损害。所以在避免死锁时,要施加较弱限制,以此获得较满意的系统性能。因为在避免死锁的策略里,允许进程动态申请资源。所以,系统在进行资源分配前会预先计算资源分配的安全性。若此次分配不会让系统进入不安全状态,就将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。

银行家算法:(详见学习笔记-银行家算法)首先要定义状态的概念,还要定义安全状态的概念。系统的状态指的是当前给进程分配资源的情况。所以,状态包含两个向量,一个是系统中每种资源的总量,另一个是未分配给进程的每种资源的总量。并且,状态还包含两个矩阵,一个是Claim,表示进程对资源的需求,另一个是表示当前分配给进程的资源。安全状态的定义是,存在至少一个资源分配序列,该序列不会引发死锁。进程请求一组资源时,先假设同意此请求,这会改变系统状态。之后要确定改变状态后的结果是否仍处于安全状态。若处于安全状态,就同意这个请求;若不是安全状态,就阻塞该进程,直到同意该请求后系统状态依旧安全 。

3)检测死锁。

这种方法不需要事先采取任何限制性措施,也不用检查系统是否进入不安全区,它允许系统在运行过程中发生死锁,可通过系统设置的检测机构,及时检测出死锁的发生,精确确定与死锁有关的进程和资源,然后采取适当措施,从系统中清除已发生的死锁 。

首先为每个进程和每个资源指定一个唯一的号码;

然后建立资源分配表和进程等待表。

4)解除死锁。

这是一种与检测死锁相配套的措施,当系统中检测到已发生死锁时,要把进程从死锁状态中解脱出来,常用的实施方法是撤销或挂起一些进程,借此回收一些资源,然后将这些资源分配给已处于阻塞状态的进程,使其转为就绪状态,从而继续运行,死锁的检测和解除措施,有可能让系统获得较好的资源利用率和吞吐量,不过在实现上难度也是最大的。

剥夺资源,即从其他进程那里剥夺足够数量的资源,将这些资源给予死锁进程,以此来解除死锁状态。

撤消进程时,可以直接撤消处于死锁状态的进程,也可以撤消代价最小的进程,持续进行此操作,直到有足够的资源可用,死锁状态被消除;这里所说的代价,指的是优先级、运行代价、进程的重要性和价值等方面。

本文的目的是分享学习笔记,部分图文源自网络,若有侵权,请联系删除。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|【宏智网络】 ( 京ICP备20013102号 )

GMT+8, 2025-5-2 22:18 , Processed in 0.074236 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表