死锁
1.产生条件
死锁的发生必须具备以下四个必要条件。
1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
2.产生原因
1.竞争资源引起进程死锁
当系统中供多个进程共享的资源如打印机、公用队列的等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。
2.进程推进顺序不当引起死锁
由于进程在运行中具有异步性特征,这可能使P1和P2两个进程按下述两种顺序向前推进。
1) 进程推进顺序合法
当进程P1 和P2并发执行时,如果按照下述顺序推进:P1:Request(R1); P1:Request(R2); P1: Relese(R1);P1: Relese(R2); P2:Request(R2); P2:Request(R1); P2: Relese(R2);P2: Relese(R1);这两个进程便可顺利完成,这种不会引起进程死锁的推进顺序是合法的。
2) 进程推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁。
3.处理方法
在系统中已经出现死锁后,应该及时检测到死锁的发生,并采取适当的措施来解除死锁。目前处理死锁的方法可归结为以下四种:
1) 预防死锁。
这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。
有序资源分配法,破坏环路
2) 避免死锁。
该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
银行家算法是一种最有代表性的避免死锁的算法。在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。
如果所有过程有可能完成执行(终止),则一个状态被认为是安全的。由于系统无法知道什么时候一个过程将终止,或者之后它需要多少资 源,系统假定所有进程将最终试图获取其声明的最大资源并在不久之后终止。在大多数情况下,这是一个合理的假设,因为系统不是特别关注每个进程运行了多久 (至少不是从避免死锁的角度)。此外,如果一个进程终止前没有获取其它能获取的最多的资源,它只是让系统更容易处理。
基于这一假设,该算法通过尝试寻找允许每个进程获得的最大资源并结束(把资源返还给系统)的进程请求的一个理想集合,来决定一个状态是否是安全的。不存在这个集合的状态都是不安全的。
该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。
这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。
3)检测死锁。
这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。
检测方法包括定时检测、效率低时检测、进程等待时检测等。
4)解除死锁。
这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
1、撤消陷于死锁的全部进程;
2、逐个撤消陷于死锁的进程,直到死锁不存在;
3、从陷于死锁的进程中逐个强迫放弃所占用的资源,直至死锁消失。
4、从另外一些进程那里强行剥夺足够数量的资源分配给死锁进程,以解除死锁状态
4.计算机网络的死锁
死锁是网络中最容易发生的故障之一,即使在网络负荷不很重时也会发生。死锁发生时,一组节点由于没有空闲缓冲区而无法接收和转发分组,节点之间相互等待,既不能接收分组也不能转发分组。
直接存储转发死锁
最常见的死锁是发生在两个节点之间的直接存储转发死锁。例如,A节点的所有缓冲区装满了等待输出到B节点的分组,而B节点的所有缓冲区也全部装满了等待输出到A节点的分组;此时,A节点不能从B节点接收分组,B节点也不能从A节点接收分组,从而造成两节点间的死锁。
一种防止存储转发死锁的方法是,每个节点设置M+1个缓冲区,并以0到M编号。M为通信子网的直径,即从任一源节点到任一目的节点间的最大链路段数。每个源节点仅当其0号缓冲区空时才能接收源端系统来的分组,而此分组仅能转发给1号缓冲区空闲的相邻节点,再由该节点将分组转发给它的2号缓冲区空闲的相邻节点……由于每个分组都是按照编号递增规则分配缓冲区,所以节点之间不会相互等待空闲缓冲区而发生死锁现象。这种方法的不足之处在于,当某节点虽然有空闲缓冲区,但正巧没有所需要的特定编号的缓冲区时,分组仍要等待,从而造成了缓冲区和链路的浪费。
另一种防止存储转发死锁的方法是,使每个分组上都携带一个全局性的惟一的"时间戳",每个节点要为每条输入链路保留一个特殊的接收缓冲区,而其它缓冲区均可用于存放中转分组。在每条输出链路的队列上分组按时间戳顺序排队。例如,节点A要将分组送到节点B,若B节点没有空闲缓冲区,但正巧有要送到A节点的分组,此时A、B节点可通过特殊的接收缓冲区交换分组;若B节点既没有空闲缓冲区,也没有要送往A节点的分组,B节点只好强行将一个出路方向大致与A节点方向相同的分组与A节点互相交换分组,但此时A节点中的分组必须比B节点中的分组具有更早的时间戳,这样才能保证子网中某个最早的分组不受阻挡地转发到目的地。由此可见,每个分组最终总会成为最早的分组,并总能被一步一步地发送到目的节点,从而避免了死锁现象的发生。
重装死锁
死锁中比较严重的情况是重装死锁。假设发给一个端系统的报文很长,被源节点拆成若干个分组发送,目的节点要将所有具有相同编号的分组重新装配成报文递交给目的端系统,若目的节点用于重装报文的缓冲区空间有限,而且它无法知道正在接收的报文究竟被拆成多少个分组,此时,就可能发生严重的问题:为了接收更多的分组,该目的节点用完了它的缓冲空间,但它又不能将尚未拼装完整的报文递送给目的端系统,而邻节点仍在不断地向它传送分组,但它却无法接收。这样,经过多次尝试后,邻节点就会绕道从其它途径再向该目的节点传送分组,但该目的节点已被死锁,其周边区域也由此发生了拥塞。
①允许目的节点将不完整的报文递交给目的端系统;
②一个不能完整重装的报文能被检测出来,并要求发送该报文的源端系统重新传送;
③为每个节点配备一个后备缓冲空间,用以暂存不完整的报文。
5.数据库死锁
降低死锁:
1)按同一顺序访问对象
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在Supplier表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。
2)避免事务中的用户交互
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
3)保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。
保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
4)使用低隔离级别
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
5)使用绑定连接
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。
oracle外键索引的死锁
在oracle数据库中,当对父表(上面例子中的dep表)进行更新的时候,如果在子表(上面例子中的student表)中的外键没有使用索引,则在更新的过程中整个子表将被锁定,而往往实际上并不需要锁定整个子表,而仅仅需要锁定子表中的几条记录。这样就会大大影响数据库访问的并发性,甚至有可能造成死锁的情况。
除了锁表的问题之外,一个没有使用索引的外键在下面两种情况下表现的也十分糟糕:
- 当使用ON DELETE CASCADE删除父表中的记录时(删除某个表的时候后面加这个关键字,会在删除这个表的同时删除和该表有关系的其他对象),如果在子表中的外键没有使用索引则当执行该操作时会对子表进行全表的扫描(无索引时搜索需要扫面全表)。因为为了保证主表记录可以删除,必须去全表扫描外键表(因为没有索引),来确定没有记录和其匹配如果匹配当然就报错不能删除。
- 对于父表和子表的连接查询,情况也是类似的。当进行这种连接查询时,如果不对外键使用索引则会发现查询的速度大大降低。
- 更新主键。
不过你还是坚持不想使用索引的话,也可以,不过必须保证下面三种情况同时满足:
- 不从父表中删除记录
- 不更新父表中的主键的值
- 一般不进行父表和子表的连接查询
参考:百科
oracle编程艺术