本文将简要说明一下几点:
- 什么是死锁?
- 死锁是怎么产生的?
- 如何解决死锁问题?
文章目录
死锁概述
一. 什么是死锁
在并发编程中,死锁问题经常会出现,从事Java开发的都应该了解过,在JDK1.7之前,在多线程环境下使用HashMap的put()操作会形成环形链表,从而使CPU利用率冲到100%,这就是一个典型的死锁问题。
死锁到底是什么呢?其实通俗来说就是:多个进程或者线程对资源发生争抢,却争抢不到而阻塞等待,空耗CPU。来看下面的这个例子:
假若有两台设备:扫描仪和CD刻录机,操作系统分配了两个进程:P1,P2。P1和P2都打算将扫描仪上扫描的内容刻录成CD光盘。进程P1先请求扫描仪获得成功,持有扫描仪;进程P2先请求CD刻录机也获得成功,持有CD刻录机。随后P1进程会去请求CD刻录机,但是由于CD刻录机正在被P2进程持有,请求失败,需要等到P2进程释放CD刻录机才可以继续申请,所以P1阻塞等待;相应的,P2也会阻塞等待。于是,两个进程都去请求对方不可释放的资源,都在阻塞等待,其结果就是一直阻塞,这就是死锁。
二. 什么是资源
并不是所有的资源都会引起死锁。在计算机系统中,只有需要采用互斥访问、不可抢占的资源才会引发死锁,这类资源被称为临界资源。例如:打印机、数据文件、队列、信号量等。计算机中的资源分类如下:
1. 可重用性资源和消耗性资源
(1) 可重用资源:可供用户重复使用的资源。特性如下:
-
每一个可重用性资源中的单元只能分配给一个进程使用,不允许多个进程共享
-
使用可重用性资源的顺序:请求资源(请求失败则阻塞等待) ——> 使用资源 ——> 释放资源
-
系统中每一类可重用资源的单元数目相对固定,进程在运行期间不能创建和删除
(2) 可消耗性资源:有被称为临时资源,是在进程运行期间,由进程动态创建和消耗。特性:资源数目可变
2. 可抢占性资源和不可抢占性资源
(1) 可抢占性资源:某进程在获得该资源后,可以被其他进程抢占。例如高优先级进程可以抢占低优先级进程的处理机。CPU和主存均属于可抢占性资源。可抢占性资源不会引起死锁。
(2) 不可抢占性资源:一旦操作系统将资源分配给某进程后,将不能强行收回资源,只能在进程使用完之后自主释放。例如磁带机、扫描仪、打印机等就属于不可抢占性资源。
三. 死锁怎样产生
1. 死锁产生的3种情况
(1) 竞争不可抢占资源引起死锁: 如下图所示,两个进程P1和P2,它们都准备写两个文件F1和F2,这两个资源属于可重用但不可抢占资源。假如两个进程并发执行,P1先打开F1和F2,操作完成后释放资源,然后P2再去申请资源,那么不会出现问题;但是假如P1打开F1,p2打开了F2,然后两个进程再去请求对方的资源,那么就会出现死锁。
(2) 竞争可消耗资源引起死锁
(3) 进程推进顺序不当引起死锁: 进程在运行过程中对资源的申请和释放的不合法也会引起死锁
2. 产生死锁的4个必要条件
(1) 互斥条件: 某一段时间内,资源只能被一个进程占用。其他进程如果想使用该资源,必须等待该进程释放。
(2) 请求和保持条件: 进程已经持有至少一个资源,但又提出新的资源请求,且请求的资源已经被其他进程占有。此时请求进程被阻塞,但自己保持的资源没有释放
(3) 不可抢占条件: 进程已经持有的资源在没有使用完成前不可被其他进程抢占,只能有进程自主释放
(4) 循环等待条件: 发生死锁时必然产生 进程——资源 循环链:P0等待P1占用的资源,p1等待P2占用的资源…Pn等待P0占用的资源。
产生死锁必须具备以上4个条件,只要有其中一个条件不成立,死锁就不会发生。
处理死锁
处理死锁的方法总结为如下4种(主要就是破坏产生死锁的必要条件):
- 预防死锁
- 避免死锁
- 检测死锁
- 解除死锁
下面详细分析者4这种方法:
1. 预防死锁
预防死锁是通过破坏产生死锁的四个必要条件中的一个或几个,以避免发生死锁。互斥条件无法破坏,所以主要是其他三个条件。
(1) 破坏请求和保持条件: 当一个进程在请求资源时,它不能持有不可抢占资源。可通过如下两个协议实现:
-
第一种协议:所有进程在开始运行前必须一次性申请其在整个运行过程中所需要的全部资源。
- 优点:简单、安全且容易实现
- 缺点:资源被严重浪费;会使进程发生饥饿现象(因为资源不足而一直等待,无法运行)
-
第二种协议:允许一个进程只获得运行初期所需要的资源后便开始运行。
(2) 破坏不可抢占条件: 当一个已经保持了某些不可抢占资源的进程,提出新的资源请求而得不到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。
(3) 破坏循环等待条件: 对系统中所有资源类型进行线性排序,并赋予不同的序号。这时可以规定协议:每个进程必须按照序号递增的顺序请求资源。
- 优点:资源利用率和吞吐量提高
- 缺点:①限制了新类型设备的增加;②会造成资源浪费;③会限制用户简单资助的编程
2. 避免死锁
避免死锁也属于事先预防的策略,但并不是事先采取某种限制措施,破坏产生死锁的必要条件,而是在资源的动态分配过程中,防止系统进入不安全状态,以避免发生死锁。该方法施加的限制条件较弱,能获得较好的系统性能,目前常用此方法来避免发生死锁。
(1) 系统状态
在死锁避免的方法中,把系统状态分为安全状态和不安全状态。当系统处于安全状态时,可避免发生死锁;当系统处于不安全状态时,可能发生死锁。
在该方法中,允许进程动态生成资源,但系统在进行资源分配前,会先计算此次资源分配的安全性:假如此次资源分配不会导致系统进入不安全状态,才会将资源分配给进程。系统能按照某种进程的推进顺序(p1,p2,p3…pn)位每个进程P分配资源,直至满足每个进程对资源的最大需求,使每个进程都可顺利的完成。此时这个推进序列(p1,p2,p3…pn)被称为安全序列。
- 安全状态: 如果系统能够找到一个安全序列,说明系统处于安全状态
- 不安全状态: 如果系统无法找到一个安全序列,说明系统处于不安全状态
当系统进入不安全状态时,有可能进入死锁状态;只要系统处于安全状态,系统便不会进入死锁状态。
(2) 银行家算法
避免死锁最具代表性的算法是银行家算法,其核心就是算出一个安全序列,使进程处于安全状态之中,从而避免死锁,其流程如下:
判断资源情况(申请资源数目<系统锁拥有的总资源数) ——> 计算安全序列 ——> 分配资源
3. 死锁检测与解除
如果上面两种措施都没有采取,系统有极大的可能进入死锁状态,在这种情况下,系统将会采取如下两种措施去解决:
- ① 死锁检测算法:检测系统状态,以确定系统是否发生了死锁
- ② 死锁解除算法:当认定系统已发生了死锁,利用死锁解除算法将系统从死锁状态中解除出来
死锁解除
如果检测出死锁,常用以下两种方法解除死锁:
- 抢占资源:从一个或多个进程中抢占足够数量的资源,分配给死锁进程,解除死锁状态
- 终止(撤销)进程:终止(或撤销)系统中的一个或多个死锁进程,直到打破循环,解除死锁状态
终止进程的方法:
- 终止所有进程
- 逐个终止进程