【Linux】什么是死锁以及如何避免死锁

介绍死锁之前,先来说一下操作系统中的资源分类

操作系统中资源的分类

  1. 按照使用次数分类
    (1)可重用性资源

每一个可重用资源中的单元只能分配给一个进程使用,不允许多个线程共享。
进程使用资源顺序:
(1) 请求资源,如果请求失败进程阻塞或循环等待;
(2) 使用资源;
(3)释放资源。
系统中的可重用资源数目都是相对固定的程序运行时不能增加或删除。

(2)消耗性资源

它是临时资源,由进程运行动态创建和消耗的,每一类消耗性资源单元数目都是不断变化的,通常在生产者线程中创建,在消费者线程中消耗。

  1. 按照能否抢占分类
    (1)可抢占资源

CPU,主存等可以共享的资源。

(2)不可抢占资源

打印机,光驱等不可共享的资源。

什么是死锁?

  1. 定义

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。
当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

  1. 产生死锁的情境
    (1)竞争不可抢占资源引起的死锁

打个比方,假设有P1和P2两个进程,都需要A和B两个资源,现在P1持有A等待B资源,而P2持有B等待A资源,两个都等待另一个资源而不肯释放资源,就这样无限等待中,这就形成死锁,这也是死锁的一种情况。给死锁下个定义,如果一组进程中每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。

(2)竞争可消耗资源引起的死锁

有p1,p2,p3三个进程,p1向p2发送消息并接受p3发送的消息,p2向p3发送消息并接受p2的消息,p3向p1发送消息并接受p2的消息,如果设置是先接到消息后发送消息,则所有的消息都不能发送,这就造成死锁。

(3)线程执行顺序不当引起的死锁

有进程p1,p2,都需要资源A,B,本来可以p1运行A –> p1运行B –> p2运行A –> p2运行B,但是顺序换了,p1运行A时p2运行B,容易发生第一种死锁。互相抢占资源。

(4)死锁的另一种:递归死锁

所谓递归函数就是自调用函数,在函数体内直接或间接的调用自己,即函数的嵌套是函数本身。
递归方式有两种:直接递归和间接递归,直接递归就是在函数中出现调用函数本身。间接递归,指函数中调用了其他函数,而该其他函数又调用了本函数。
那什么时候使用递归呢?一般来说当你要在某段代码逻辑中使用循环迭代的时候但是迭代的次数在迭代之前无法知晓的情况下使用递归。
打个比方你要在一个文件夹中查找某个文件,而这个文件夹底下有N多子文件夹和文件,当你在不知道有多少层文件夹和文件的情况下你就得用到递归了。
递归的优点就是让代码显得很简洁,同时有些应用场景不得不使用递归比如前面说的找文件。
递归是个好东西但是在某些时候也会给你带来一些麻烦。比如在多线程的环境下使用递归,遇到了多线程那么就不得不面对同步的问题。
而递归程序遇到同步的时候很容易出问题。多线程的递归就是指递归链中的某个方法由另外一个线程来操作。(如果是在一个线程中就不存在死锁问题)

下面来总结一下产生死锁的四个必要条件

产生死锁的四个必要条件

1.互斥条件

进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放

2.请求和保持条件

一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。

3.不剥夺条件

任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用

4.循环等待条件

当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

如何避免死锁

下面来说明一下如何避免死锁的产生
1、预防死锁
上面介绍了死锁产生的四个必要条件,那如何避免死锁的产生呢?只要破坏上面四个必要条件其中之一,就不会发生死锁。
2、避免死锁
在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。

预防死锁和避免死锁都属于事先预防策略,但预防死锁的限制条件比较严格,实现起来 较为简单,但往往导致系统的效率低,资源利用率低;
避免死锁的限制条件相对宽松,资源 分配后需要通过算法来判断是否进入不安全状态,实现起来较为复杂。

3、死锁的检测
这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。
4、解除死锁
这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。

  • 死锁的处理策略
    死锁的处理策略:鸵鸟策略、预防策略、避免策略、检测与恢复策略
  • 三种避免死锁的技术
    (1)加锁顺序
    (2)加锁时限
    (3)死锁检测
    参考这篇博文
  • 常见算法
    银行家算法
    参考这篇博文

猜你喜欢

转载自blog.csdn.net/weixin_38682277/article/details/80053006